Ecocentral/ecocentral#152: include in_refund invoices. rappel_pending readonly. vendor ref in invoice lines
This commit is contained in:
parent
4c07dd042d
commit
29ef56b800
5 changed files with 140 additions and 18 deletions
|
|
@ -16,13 +16,16 @@ class AccountMove(models.Model):
|
|||
def create(self, vals_list):
|
||||
moves = super().create(vals_list)
|
||||
for move in moves:
|
||||
if move.move_type == "in_invoice" and move.partner_id.rappel_commission:
|
||||
if (
|
||||
move.move_type in ("in_invoice", "in_refund")
|
||||
and move.partner_id.rappel_commission
|
||||
):
|
||||
move.rappel_pending = True
|
||||
return moves
|
||||
|
||||
@api.onchange("partner_id")
|
||||
def _onchange_partner_id_rappel(self):
|
||||
if self.move_type == "in_invoice":
|
||||
if self.move_type in ("in_invoice", "in_refund"):
|
||||
self.rappel_pending = bool(
|
||||
self.partner_id and self.partner_id.rappel_commission
|
||||
)
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class TestPurchaseRappel(AccountTestInvoicingCommon):
|
|||
}
|
||||
)
|
||||
|
||||
def _create_vendor_bill(self, partner, amount=1000.0, post=True):
|
||||
def _create_vendor_bill(self, partner, amount=1000.0, post=True, ref=None):
|
||||
move = self.init_invoice(
|
||||
"in_invoice",
|
||||
partner=partner,
|
||||
|
|
@ -77,6 +77,22 @@ class TestPurchaseRappel(AccountTestInvoicingCommon):
|
|||
post=False,
|
||||
)
|
||||
move.invoice_line_ids[0].price_unit = amount
|
||||
if ref is not None:
|
||||
move.ref = ref
|
||||
if post:
|
||||
move.action_post()
|
||||
return move
|
||||
|
||||
def _create_vendor_refund(self, partner, amount=200.0, post=True, ref=None):
|
||||
move = self.init_invoice(
|
||||
"in_refund",
|
||||
partner=partner,
|
||||
products=self.product_a,
|
||||
post=False,
|
||||
)
|
||||
move.invoice_line_ids[0].price_unit = amount
|
||||
if ref is not None:
|
||||
move.ref = ref
|
||||
if post:
|
||||
move.action_post()
|
||||
return move
|
||||
|
|
@ -236,3 +252,82 @@ class TestPurchaseRappel(AccountTestInvoicingCommon):
|
|||
)
|
||||
with self.assertRaises(UserError):
|
||||
wizard.action_create_rappel_invoices()
|
||||
|
||||
def test_wizard_vendor_ref_in_description(self):
|
||||
bill_with_ref = self._create_vendor_bill(
|
||||
self.vendor_rappel, amount=1000.0, ref="VENDOR-REF-001"
|
||||
)
|
||||
wizard = (
|
||||
self.env["account.move.rappel.wizard"]
|
||||
.with_context(active_ids=bill_with_ref.ids)
|
||||
.create({})
|
||||
)
|
||||
action = wizard.action_create_rappel_invoices()
|
||||
rappel_invoice = self.env["account.move"].browse(action["res_id"])
|
||||
line_name = rappel_invoice.invoice_line_ids[0].name
|
||||
self.assertIn("VENDOR-REF-001", line_name)
|
||||
self.assertNotIn(bill_with_ref.name, line_name)
|
||||
|
||||
bill_without_ref = self._create_vendor_bill(
|
||||
self.vendor_rappel, amount=1000.0, ref=None
|
||||
)
|
||||
wizard2 = (
|
||||
self.env["account.move.rappel.wizard"]
|
||||
.with_context(active_ids=bill_without_ref.ids)
|
||||
.create({})
|
||||
)
|
||||
action2 = wizard2.action_create_rappel_invoices()
|
||||
rappel_invoice2 = self.env["account.move"].browse(action2["res_id"])
|
||||
line_name2 = rappel_invoice2.invoice_line_ids[0].name
|
||||
self.assertIn(bill_without_ref.name, line_name2)
|
||||
|
||||
def test_wizard_includes_in_refund(self):
|
||||
bill = self._create_vendor_bill(self.vendor_rappel, amount=1000.0)
|
||||
refund = self._create_vendor_refund(self.vendor_rappel, amount=200.0)
|
||||
self.assertTrue(refund.rappel_pending)
|
||||
|
||||
wizard = (
|
||||
self.env["account.move.rappel.wizard"]
|
||||
.with_context(active_ids=(bill + refund).ids)
|
||||
.create({})
|
||||
)
|
||||
action = wizard.action_create_rappel_invoices()
|
||||
rappel_invoice = self.env["account.move"].browse(action["res_id"])
|
||||
|
||||
self.assertEqual(len(rappel_invoice.invoice_line_ids), 2)
|
||||
amounts = sorted(rappel_invoice.invoice_line_ids.mapped("price_unit"))
|
||||
self.assertEqual(amounts, [-10.0, 50.0])
|
||||
self.assertFalse(bill.rappel_pending)
|
||||
self.assertFalse(refund.rappel_pending)
|
||||
|
||||
def test_wizard_refund_subtracts_commission(self):
|
||||
bill = self._create_vendor_bill(self.vendor_rappel, amount=1000.0)
|
||||
refund = self._create_vendor_refund(self.vendor_rappel, amount=200.0)
|
||||
|
||||
wizard = (
|
||||
self.env["account.move.rappel.wizard"]
|
||||
.with_context(active_ids=(bill + refund).ids)
|
||||
.create({})
|
||||
)
|
||||
action = wizard.action_create_rappel_invoices()
|
||||
rappel_invoice = self.env["account.move"].browse(action["res_id"])
|
||||
|
||||
self.assertEqual(len(rappel_invoice.invoice_line_ids), 2)
|
||||
line_by_sign = {
|
||||
line.price_unit > 0: line for line in rappel_invoice.invoice_line_ids
|
||||
}
|
||||
self.assertEqual(line_by_sign[True].price_unit, 50.0)
|
||||
self.assertEqual(line_by_sign[False].price_unit, -10.0)
|
||||
self.assertEqual(rappel_invoice.amount_untaxed, 40.0)
|
||||
self.assertIn("Refund", line_by_sign[False].name)
|
||||
|
||||
def test_rappel_pending_set_on_refund_create(self):
|
||||
refund = self._create_vendor_refund(
|
||||
self.vendor_rappel, amount=200.0, post=False
|
||||
)
|
||||
self.assertTrue(refund.rappel_pending)
|
||||
|
||||
refund_no_rappel = self._create_vendor_refund(
|
||||
self.vendor_no_rappel, amount=200.0, post=False
|
||||
)
|
||||
self.assertFalse(refund_no_rappel.rappel_pending)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,9 @@
|
|||
<field name="to_check" position="after">
|
||||
<field name="rappel_pending"
|
||||
optional="show"
|
||||
readonly="1"
|
||||
widget="boolean_toggle"
|
||||
column_invisible="context.get('default_move_type') != 'in_invoice'"/>
|
||||
column_invisible="context.get('default_move_type') not in ('in_invoice', 'in_refund')"/>
|
||||
</field>
|
||||
</field>
|
||||
</record>
|
||||
|
|
@ -40,7 +41,8 @@
|
|||
<field name="arch" type="xml">
|
||||
<field name="to_check" position="after">
|
||||
<field name="rappel_pending"
|
||||
invisible="move_type != 'in_invoice'"
|
||||
invisible="move_type not in ('in_invoice', 'in_refund')"
|
||||
readonly="1"
|
||||
widget="boolean_toggle"/>
|
||||
</field>
|
||||
</field>
|
||||
|
|
|
|||
|
|
@ -49,16 +49,17 @@ class AccountMoveRappelWizard(models.TransientModel):
|
|||
active_ids = self.env.context.get("active_ids", [])
|
||||
moves = self.env["account.move"].browse(active_ids)
|
||||
|
||||
# Filter only posted vendor bills
|
||||
# Filter only posted vendor bills and refunds
|
||||
valid_moves = moves.filtered(
|
||||
lambda m: m.move_type == "in_invoice" and m.state == "posted"
|
||||
lambda m: m.move_type in ("in_invoice", "in_refund")
|
||||
and m.state == "posted"
|
||||
)
|
||||
if not valid_moves:
|
||||
raise UserError(
|
||||
_(
|
||||
"No posted vendor bills found in the selection. "
|
||||
"Only posted (validated) vendor bills can be used to create "
|
||||
"rappel commission invoices."
|
||||
"No posted vendor bills or refunds found in the selection. "
|
||||
"Only posted (validated) vendor bills and refunds can be used to "
|
||||
"create rappel commission invoices."
|
||||
)
|
||||
)
|
||||
|
||||
|
|
@ -135,11 +136,28 @@ class AccountMoveRappelWizard(models.TransientModel):
|
|||
for bill in bills:
|
||||
taxable_base = bill.amount_untaxed
|
||||
commission_amount = taxable_base * rate / 100.0
|
||||
vendor_ref = bill.ref or bill.name
|
||||
if bill.move_type == "in_refund":
|
||||
# Vendor refunds reduce the commission: negate the amount so
|
||||
# the resulting line subtracts from the rappel invoice total.
|
||||
# amount_untaxed is always positive in Odoo, so we flip the
|
||||
# sign here and display a negative base in the description.
|
||||
commission_amount = -commission_amount
|
||||
display_base = -taxable_base
|
||||
name = _(
|
||||
"Rappel commission %(rate)s%% — Refund %(bill)s "
|
||||
"(taxable base: %(base)s %(currency)s)",
|
||||
rate=rate,
|
||||
bill=vendor_ref,
|
||||
base=f"{display_base:,.2f}",
|
||||
currency=bill.currency_id.name,
|
||||
)
|
||||
else:
|
||||
name = _(
|
||||
"Rappel commission %(rate)s%% — Bill %(bill)s "
|
||||
"(taxable base: %(base)s %(currency)s)",
|
||||
rate=rate,
|
||||
bill=bill.name,
|
||||
bill=vendor_ref,
|
||||
base=f"{taxable_base:,.2f}",
|
||||
currency=bill.currency_id.name,
|
||||
)
|
||||
|
|
|
|||
|
|
@ -24,6 +24,10 @@
|
|||
<field name="move_ids">
|
||||
<tree string="Vendor Bills" editable="0">
|
||||
<field name="name"/>
|
||||
<field name="move_type"
|
||||
widget="badge"
|
||||
decoration-info="move_type == 'in_invoice'"
|
||||
decoration-danger="move_type == 'in_refund'"/>
|
||||
<field name="partner_id"/>
|
||||
<field name="invoice_date"/>
|
||||
<field name="amount_untaxed"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue