Skip to content

Commit cedb91d

Browse files
committed
[FIX] account_mass_reconcile: reconcile with writeoff currency and opposite signs
1 parent b73695a commit cedb91d

File tree

2 files changed

+138
-1
lines changed

2 files changed

+138
-1
lines changed

account_mass_reconcile/models/base_reconciliation.py

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,9 +158,20 @@ def create_write_off(self, lines, amount, amount_curr, same_curr):
158158
currency = same_curr and lines[0].currency_id or lines[0].company_id.currency_id
159159
journal = self.journal_id
160160
partners = lines.mapped("partner_id")
161+
162+
# Adjust amount_currency to match the sign of the company's residual (amount)
163+
if same_curr:
164+
if amount != 0:
165+
sign = 1 if amount > 0 else -1
166+
adjusted_amount_curr = abs(amount_curr) * sign
167+
else:
168+
adjusted_amount_curr = 0.0
169+
else:
170+
adjusted_amount_curr = amount
171+
161172
write_off_vals = {
162173
"name": _("Automatic writeoff"),
163-
"amount_currency": same_curr and amount_curr or amount,
174+
"amount_currency": adjusted_amount_curr,
164175
"debit": amount > 0.0 and amount or 0.0,
165176
"credit": amount < 0.0 and -amount or 0.0,
166177
"partner_id": len(partners) == 1 and partners.id or False,

account_mass_reconcile/tests/test_scenario_reconcile.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -451,3 +451,129 @@ def test_reconcile_with_writeoff_today(self):
451451
)
452452
self.assertEqual(len(writeoff_line), 1)
453453
self.assertEqual(writeoff_line.date, fields.Date.today())
454+
455+
def test_reconcile_with_writeoff_currency_opposite_signs(self):
456+
"""Test write-off when company and foreign residuals have opposite signs."""
457+
self.env.ref("base.group_multi_currency").users |= self.env.user
458+
self.company.currency_id = self.env.ref("base.EUR")
459+
usd = self.env.ref("base.USD")
460+
usd.active = True
461+
462+
inv_date = fields.Date.today() - timedelta(days=10)
463+
pay_date = fields.Date.today() - timedelta(days=1)
464+
465+
# Create currency rates
466+
self.env["res.currency.rate"].create(
467+
[
468+
{
469+
"name": inv_date,
470+
"currency_id": usd.id,
471+
"rate": 1.2,
472+
"company_id": self.company.id,
473+
},
474+
{
475+
"name": pay_date,
476+
"rate": 1.25,
477+
"currency_id": usd.id,
478+
"company_id": self.company.id,
479+
},
480+
{
481+
"name": fields.Date.today(),
482+
"rate": 1.25,
483+
"currency_id": usd.id,
484+
"company_id": self.company.id,
485+
},
486+
]
487+
)
488+
489+
# Create invoice and payment with opposing residual signs
490+
common_ref = "INV-PAY-OPPOSITE-SIGNS"
491+
invoice = self.init_invoice(
492+
move_type="out_invoice",
493+
partner=self.partner_a,
494+
currency=usd,
495+
amounts=[100],
496+
invoice_date=inv_date,
497+
post=True,
498+
)
499+
invoice.ref = common_ref
500+
501+
payment = self.env["account.payment"].create(
502+
{
503+
"partner_type": "customer",
504+
"payment_type": "inbound",
505+
"partner_id": self.partner_a.id,
506+
"destination_account_id": self.company_data[
507+
"default_account_receivable"
508+
].id,
509+
"amount": 100.10,
510+
"currency_id": usd.id,
511+
"journal_id": self.bank_journal.id,
512+
"date": pay_date,
513+
"ref": common_ref,
514+
}
515+
)
516+
payment.action_post()
517+
518+
# Create and run mass reconciliation
519+
mass_rec = self.mass_rec_obj.create(
520+
{
521+
"name": "mass_reconcile_currency_writeoff_opposite",
522+
"account": self.company_data["default_account_receivable"].id,
523+
"reconcile_method": [
524+
(
525+
0,
526+
0,
527+
{
528+
"name": "mass.reconcile.advanced.ref",
529+
"account_lost_id": self.company_data[
530+
"default_account_expense"
531+
].id,
532+
"account_profit_id": self.company_data[
533+
"default_account_revenue"
534+
].id,
535+
"journal_id": self.company_data["default_journal_misc"].id,
536+
"write_off": 3.50,
537+
"date_base_on": "newest",
538+
},
539+
)
540+
],
541+
}
542+
)
543+
existing_moves = self.env["account.move"].search(
544+
[("journal_id", "=", self.company_data["default_journal_misc"].id)]
545+
)
546+
mass_rec.run_reconcile()
547+
548+
# Validate write-off move
549+
writeoff_move = self.env["account.move"].search(
550+
[
551+
("journal_id", "=", self.company_data["default_journal_misc"].id),
552+
("id", "not in", existing_moves.ids),
553+
]
554+
)
555+
self.assertEqual(
556+
len(writeoff_move), 1, "Exactly one write-off move should exist"
557+
)
558+
self.assertEqual(
559+
writeoff_move.currency_id, usd, "USD should be the write-off currency"
560+
)
561+
562+
pl_line = writeoff_move.line_ids.filtered(
563+
lambda ml: ml.account_id == self.company_data["default_account_expense"]
564+
)
565+
receivable_line = writeoff_move.line_ids.filtered(
566+
lambda ml: ml.account_id == self.company_data["default_account_receivable"]
567+
)
568+
569+
# Check amounts (0.10 USD residual, 3.25 EUR residual)
570+
self.assertAlmostEqual(pl_line.debit, 3.25, 2, "Expense line debit (EUR)")
571+
self.assertAlmostEqual(
572+
pl_line.amount_currency, 0.10, 2, "Expense line USD amount"
573+
)
574+
self.assertAlmostEqual(
575+
receivable_line.credit, 3.25, 2, "Receivable line credit (EUR)"
576+
)
577+
self.assertAlmostEqual(
578+
receivable_line.amount_currency, -0.10, 2, "Receivable line USD amount"
579+
)

0 commit comments

Comments
 (0)