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

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 

6 

7 

8class AccountPaymentInvoiceWizard(models.TransientModel): 

9 _inherit = "analytic.mixin" 

10 _name = "account.payment.invoice.wizard" 

11 _description = "account.payment.invoice.wizard" 

12 

13 @api.model 

14 def default_payment_group(self): 

15 return self.env["account.payment"].browse(self.env.context.get("active_id", False)) 

16 

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() 

74 

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() 

88 

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 } 

114 

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) 

125 

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 

140 

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 

162 

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 } 

184 

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_" 

192 

193 if self.env.context.get("refund"): 

194 invoice_type += "refund" 

195 else: 

196 invoice_type += "invoice" 

197 

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 } 

210 

211 def confirm(self): 

212 self.ensure_one() 

213 

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() 

229 

230 self.payment_id.to_pay_move_line_ids += invoice.open_move_line_ids 

231 

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 

238 

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