Coverage for ingadhoc-account-payment / account_payment_pro / wizards / account_payment_invoice_wizard.py: 29%
116 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 19:54 +0000
« prev ^ index » next coverage.py v7.13.4, created at 2026-03-09 19:54 +0000
1##############################################################################
2# For copyright and license notices, see __manifest__.py file in root directory
3##############################################################################
4from odoo import _, api, fields, models
5from odoo.exceptions import ValidationError
8class AccountPaymentInvoiceWizard(models.TransientModel):
9 _inherit = "analytic.mixin"
10 _name = "account.payment.invoice.wizard"
11 _description = "account.payment.invoice.wizard"
13 @api.model
14 def default_payment_group(self):
15 return self.env["account.payment"].browse(self.env.context.get("active_id", False))
17 payment_id = fields.Many2one(
18 "account.payment",
19 default=default_payment_group,
20 ondelete="cascade",
21 required=True,
22 )
23 journal_id = fields.Many2one(
24 "account.journal",
25 "Journal",
26 required=True,
27 ondelete="cascade",
28 )
29 available_journal_ids = fields.Many2many(comodel_name="account.journal", compute="_compute_available_journal_ids")
30 invoice_date = fields.Date(string="Refund Date", default=fields.Date.context_today, required=True)
31 currency_id = fields.Many2one(
32 related="payment_id.currency_id",
33 )
34 date = fields.Date(string="Accounting Date")
35 product_id = fields.Many2one(
36 "product.product",
37 required=True,
38 domain=[("sale_ok", "=", True)],
39 )
40 product_account_id = fields.Many2one("account.account")
41 tax_ids = fields.Many2many(
42 "account.tax",
43 string="Taxes",
44 )
45 amount_untaxed = fields.Monetary(
46 string="Untaxed Amount",
47 required=True,
48 compute="_compute_amount_untaxed",
49 inverse="_inverse_amount_untaxed",
50 )
51 # we make amount total the main one and the other computed because the
52 # normal case of use would be to know the total amount and also this amount
53 # is the suggested one on creating the wizard
54 amount_total = fields.Monetary(string="Total Amount", required=True)
55 description = fields.Char(
56 string="Reason",
57 )
58 company_id = fields.Many2one(
59 related="payment_id.company_id",
60 )
61 use_documents = fields.Boolean(
62 related="journal_id.l10n_latam_use_documents",
63 string="Use Documents?",
64 )
65 journal_document_type_id = fields.Many2one(
66 "l10n_latam.document.type",
67 "Document Type",
68 ondelete="cascade",
69 )
70 l10n_latam_manual_document_number = fields.Boolean(
71 compute="_compute_l10n_latam_manual_document_number", string="Manual Number"
72 )
73 document_number = fields.Char()
75 @api.depends("journal_document_type_id")
76 def _compute_l10n_latam_manual_document_number(self):
77 for rec in self:
78 refund = rec.env["account.move"].new(
79 {
80 "move_type": self.get_invoice_vals().get("move_type"),
81 "journal_id": rec.journal_id.id,
82 "partner_id": rec.payment_id.partner_id.id,
83 "company_id": rec.payment_id.company_id.id,
84 "l10n_latam_document_type_id": rec.journal_document_type_id.id,
85 }
86 )
87 rec.l10n_latam_manual_document_number = refund._is_manual_document_number()
89 @api.onchange("journal_id")
90 def _onchange_journal_id(self):
91 if self.journal_id.l10n_latam_use_documents:
92 refund = self.env["account.move"].new(
93 {
94 "move_type": self.get_invoice_vals().get("move_type"),
95 "journal_id": self.journal_id.id,
96 "partner_id": self.payment_id.partner_id.id,
97 "company_id": self.payment_id.company_id.id,
98 }
99 )
100 if self.env.context.get("internal_type") == "debit_note":
101 document_types = refund.l10n_latam_available_document_type_ids.filtered(
102 lambda x: x.internal_type == "debit_note"
103 )
104 self.journal_document_type_id = (
105 document_types and document_types[0]._origin or refund.l10n_latam_document_type_id
106 )
107 else:
108 self.journal_document_type_id = refund.l10n_latam_document_type_id
109 return {
110 "domain": {
111 "journal_document_type_id": [("id", "in", refund.l10n_latam_available_document_type_ids.ids)]
112 }
113 }
115 @api.onchange("product_id")
116 def change_product(self):
117 self.ensure_one()
118 if self.payment_id.partner_type == "supplier":
119 taxes = self.product_id.supplier_taxes_id
120 else:
121 taxes = self.product_id.taxes_id
122 company = self.company_id or self.env.company
123 taxes = taxes.filtered(lambda r: r.company_id == company)
124 self.tax_ids = self.payment_id.partner_id.with_company(company).property_account_position_id.map_tax(taxes)
126 @api.onchange("amount_untaxed", "tax_ids")
127 def _inverse_amount_untaxed(self):
128 self.ensure_one()
129 if self.tax_ids:
130 taxes = self.tax_ids.compute_all(
131 self.amount_untaxed,
132 self.company_id.currency_id,
133 1.0,
134 product=self.product_id,
135 partner=self.payment_id.partner_id,
136 )
137 self.amount_total = taxes["total_included"]
138 else:
139 self.amount_total = self.amount_untaxed
141 @api.depends("tax_ids", "amount_total")
142 def _compute_amount_untaxed(self):
143 """
144 For now we implement inverse only for percent taxes. We could extend to
145 other by simulating tax.price_include = True, computing tax and
146 then restoring tax.price_include = False.
147 """
148 self.ensure_one()
149 tax_percent = 0.0
150 for tax in self.tax_ids.filtered(lambda x: not x.price_include and x.amount_type not in ["fixed"] or x.amount):
151 if tax.amount_type == "percent":
152 tax_percent += tax.amount
153 elif tax.amount_type == "partner_tax":
154 # ugly compatibility with l10n_ar l10n_ar_account_withholding
155 tax_percent += tax.get_partner_alicuot(
156 self.payment_id.partner_id, fields.Date.context_today(self)
157 ).alicuota_percepcion
158 else:
159 raise ValidationError(_("You can only set amount total if taxes are of type " "percentage"))
160 total_percent = (1 + tax_percent / 100) or 1.0
161 self.amount_untaxed = self.amount_total / total_percent
163 @api.onchange("payment_id")
164 def change_payment_group(self):
165 journal_type = "sale"
166 type_tax_use = "sale"
167 if self.payment_id.partner_type == "supplier":
168 journal_type = "purchase"
169 type_tax_use = "purchase"
170 journal_domain = [
171 ("type", "=", journal_type),
172 ("company_id", "=", self.payment_id.company_id.id),
173 ]
174 tax_domain = [("type_tax_use", "=", type_tax_use), ("company_id", "=", self.payment_id.company_id.id)]
175 self.journal_id = self.env["account.journal"].search(journal_domain, limit=1)
176 # usually debit/credit note will be for the payment difference
177 self.amount_total = abs(self.payment_id.payment_difference)
178 return {
179 "domain": {
180 "journal_id": journal_domain,
181 "tax_ids": tax_domain,
182 }
183 }
185 def get_invoice_vals(self):
186 self.ensure_one()
187 payment_group = self.payment_id
188 if payment_group.partner_type == "supplier":
189 invoice_type = "in_"
190 else:
191 invoice_type = "out_"
193 if self.env.context.get("refund"):
194 invoice_type += "refund"
195 else:
196 invoice_type += "invoice"
198 return {
199 "ref": self.description,
200 "date": self.date or self.invoice_date or fields.Date.context_today(self),
201 "invoice_date": self.invoice_date,
202 "invoice_origin": _("Payment id %s") % payment_group.id,
203 "journal_id": self.journal_id.id,
204 "invoice_user_id": payment_group.partner_id.user_id.id,
205 "partner_id": payment_group.partner_id.id,
206 "move_type": invoice_type,
207 "l10n_latam_document_type_id": self.journal_document_type_id.id,
208 "l10n_latam_document_number": self.document_number,
209 }
211 def confirm(self):
212 self.ensure_one()
214 self = self.with_company(self.company_id).with_context(company_id=self.company_id.id)
215 invoice_vals = self.get_invoice_vals()
216 line_vals = {
217 "product_id": self.product_id.id,
218 "price_unit": self.amount_untaxed,
219 "tax_ids": [(6, 0, self.tax_ids.ids)],
220 }
221 # force the account off the product
222 if self.product_account_id:
223 line_vals["account_id"] = self.product_account_id.id
224 if self.analytic_distribution:
225 line_vals["analytic_distribution"] = self.analytic_distribution
226 invoice_vals["invoice_line_ids"] = [(0, 0, line_vals)]
227 invoice = self.env["account.move"].create(invoice_vals)
228 invoice.action_post()
230 self.payment_id.to_pay_move_line_ids += invoice.open_move_line_ids
232 @api.onchange("document_number", "journal_document_type_id")
233 def _onchange_document_number(self):
234 if self.journal_document_type_id:
235 document_number = self.journal_document_type_id._format_document_number(self.document_number)
236 if self.document_number != document_number:
237 self.document_number = document_number
239 @api.depends("payment_id.partner_type")
240 def _compute_available_journal_ids(self):
241 journal_type = "sale"
242 if self.payment_id.partner_type == "supplier":
243 journal_type = "purchase"
244 journal_domain = [
245 ("type", "=", journal_type),
246 ("company_id", "=", self.payment_id.company_id.id),
247 ]
248 self.available_journal_ids = self.env["account.journal"].search(journal_domain).ids