feat(portal): account_summary controller + 3 unit tests

New /my/account_summary route. Splits posted account.move into
Invoices (out_invoice) / Credit Memos (out_refund) / Statements
(V1 placeholder). Open Balance helper sums amount_residual across
open invoices for the partner's commercial tree.

Search filters name OR ref (customer PO). Sort options: date desc/asc,
amount desc/asc. Filter pills: open / closed / all.

Tests cover the tab partitioning, the open-balance sum, and the
search behaviour. Helpers use commercial_partner.env so they work
in both HTTP context and unit tests without requiring request.env.
Test scaffolding uses fp_from_so_invoice=True context flag and
invoice_payment_term_id to satisfy the fusion_plating_jobs and
fusion_plating_invoicing create/post gates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
gsinghpal
2026-05-17 14:13:48 -04:00
parent 8225061dfa
commit b92a396934
2 changed files with 246 additions and 0 deletions

View File

@@ -147,3 +147,124 @@ class TestPortalDashboard(TransactionCase):
# Work Order is placeholder without a backend fp.job link
wo = next(g for g in groups if g['key'] == 'work_order')
self.assertTrue(all(d.get('pending') for d in wo['docs']))
def test_account_summary_partitions_invoices_and_credits(self):
"""Account Summary helper splits posted moves by move_type."""
from odoo.addons.fusion_plating_portal.controllers.portal import FpCustomerPortal
# fp_from_so_invoice=True bypasses the fusion_plating_jobs enforcement
# that normally requires invoices to originate from a Sale Order.
# payment_term is required by fusion_plating_invoicing's action_post gate.
# Both are test-data scaffolding only; they do not affect what is tested.
pt = self.env.ref('account.account_payment_term_immediate')
Move = self.env['account.move'].with_context(fp_from_so_invoice=True)
inv = Move.create({
'partner_id': self.partner.id,
'move_type': 'out_invoice',
'invoice_date': '2026-05-01',
'invoice_payment_term_id': pt.id,
'invoice_line_ids': [(0, 0, {
'name': 'Test plating',
'quantity': 1,
'price_unit': 250.00,
})],
})
inv.action_post()
cm = Move.create({
'partner_id': self.partner.id,
'move_type': 'out_refund',
'invoice_date': '2026-05-02',
'invoice_payment_term_id': pt.id,
'invoice_line_ids': [(0, 0, {
'name': 'Test credit',
'quantity': 1,
'price_unit': 50.00,
})],
})
cm.action_post()
controller = FpCustomerPortal()
data = controller._fp_account_summary_data(
self.partner.commercial_partner_id,
tab='invoices',
filter_state='all',
search='',
sort='date_desc',
page=1,
)
# Tab=invoices -> only out_invoice
names = data['records'].mapped('name')
self.assertIn(inv.name, names)
self.assertNotIn(cm.name, names)
data = controller._fp_account_summary_data(
self.partner.commercial_partner_id,
tab='credit_memos',
filter_state='all',
search='',
sort='date_desc',
page=1,
)
names = data['records'].mapped('name')
self.assertIn(cm.name, names)
self.assertNotIn(inv.name, names)
def test_account_summary_open_balance_sums_residuals(self):
"""Open Balance pill = sum of amount_residual across open invoices."""
from odoo.addons.fusion_plating_portal.controllers.portal import FpCustomerPortal
pt = self.env.ref('account.account_payment_term_immediate')
Move = self.env['account.move'].with_context(fp_from_so_invoice=True)
inv = Move.create({
'partner_id': self.partner.id,
'move_type': 'out_invoice',
'invoice_date': '2026-05-01',
'invoice_payment_term_id': pt.id,
'invoice_line_ids': [(0, 0, {
'name': 'Open inv',
'quantity': 1,
'price_unit': 750.00,
})],
})
inv.action_post()
controller = FpCustomerPortal()
open_balance = controller._fp_account_summary_open_balance(
self.partner.commercial_partner_id,
)
# The 750 invoice has amount_residual = 750 until paid
self.assertEqual(open_balance, 750.00)
def test_account_summary_search_matches_name_and_ref(self):
"""Search box filters by invoice number OR customer PO (ref)."""
from odoo.addons.fusion_plating_portal.controllers.portal import FpCustomerPortal
pt = self.env.ref('account.account_payment_term_immediate')
Move = self.env['account.move'].with_context(fp_from_so_invoice=True)
inv = Move.create({
'partner_id': self.partner.id,
'move_type': 'out_invoice',
'invoice_date': '2026-05-01',
'invoice_payment_term_id': pt.id,
'ref': 'PO-CUSTOMER-99999',
'invoice_line_ids': [(0, 0, {
'name': 'Sale',
'quantity': 1,
'price_unit': 100.0,
})],
})
inv.action_post()
controller = FpCustomerPortal()
# Search by ref (customer PO)
data = controller._fp_account_summary_data(
self.partner.commercial_partner_id,
tab='invoices', filter_state='all',
search='99999', sort='date_desc', page=1,
)
self.assertIn(inv, data['records'])
# Search that matches nothing
data = controller._fp_account_summary_data(
self.partner.commercial_partner_id,
tab='invoices', filter_state='all',
search='zzznotfoundzzz', sort='date_desc', page=1,
)
self.assertNotIn(inv, data['records'])