From 7a66d7849dbdfc9f51e7a3b7c701160136ad0e5a Mon Sep 17 00:00:00 2001 From: gsinghpal Date: Wed, 27 May 2026 17:24:48 -0400 Subject: [PATCH] fix(billing): name ledger partners by company, not the NexaCloud user's full_name One operator (e.g. "Gurpreet Singh") manages several distinct customer businesses; naming partners from full_name mislabeled Mobility Specialties Inc and Apex Vita Corporation as "Gurpreet Singh". Read the company field, name the partner by company (mark is_company), and rewrite existing partners so prior full_name-based names are corrected on re-ingest. 75 tests green. --- .../tests/test_invoice_ledger.py | 8 ++++++++ .../wizards/invoice_ledger.py | 19 +++++++++++++++---- 2 files changed, 23 insertions(+), 4 deletions(-) diff --git a/fusion_centralize_billing/tests/test_invoice_ledger.py b/fusion_centralize_billing/tests/test_invoice_ledger.py index 57c2ba4c..ec769e8f 100644 --- a/fusion_centralize_billing/tests/test_invoice_ledger.py +++ b/fusion_centralize_billing/tests/test_invoice_ledger.py @@ -120,6 +120,14 @@ class TestLedgerIngest(TransactionCase): self.assertAlmostEqual(mv.amount_untaxed, 200.0, places=2) # captured via reconciling line self.assertTrue(any('base/unitemized' in (l.name or '') for l in mv.invoice_line_ids)) + def test_partner_named_by_company_not_person(self): + data = _inv_fixture() + data[0]['partner_company'] = 'Acme Holdings Inc' # full_name is "Acme"; company wins + self.W._ingest_invoices(data, post=False) + mv = self.Move.search([('x_fc_nexacloud_invoice_id', '=', 'inv-1')]) + self.assertEqual(mv.partner_id.name, 'Acme Holdings Inc') + self.assertTrue(mv.partner_id.is_company) + def test_prune_shadow_removes_shadow_subs_only(self): p = self.env['res.partner'].sudo().create({'name': 'X'}) shadow = self.env['sale.order'].sudo().create({'partner_id': p.id, 'x_fc_shadow': True}) diff --git a/fusion_centralize_billing/wizards/invoice_ledger.py b/fusion_centralize_billing/wizards/invoice_ledger.py index 4b5aab45..922fefab 100644 --- a/fusion_centralize_billing/wizards/invoice_ledger.py +++ b/fusion_centralize_billing/wizards/invoice_ledger.py @@ -79,6 +79,7 @@ class FusionBillingInvoiceLedgerWizard(models.TransientModel): cur.execute( "SELECT i.id, i.stripe_invoice_id, i.invoice_number, " "i.user_id AS user_external_id, u.full_name AS partner_name, " + "u.company AS partner_company, " "COALESCE(u.billing_email, u.email) AS partner_email, " "i.created_at AS invoice_date, i.currency, i.status, i.subtotal, i.tax, " "i.amount_paid, i.paid_at " @@ -125,13 +126,16 @@ class FusionBillingInvoiceLedgerWizard(models.TransientModel): if existing and existing.state != "draft": summary["skipped"].append({"id": nc_id, "reason": "already posted"}) continue + partner = self._fc_partner_for(inv) if existing: existing.invoice_line_ids.unlink() # draft: replace lines + if existing.partner_id != partner: + existing.partner_id = partner.id move = existing else: move = Move.create({ "move_type": "out_invoice", - "partner_id": self._fc_partner_for(inv).id, + "partner_id": partner.id, "invoice_date": inv.get("invoice_date"), "ref": inv.get("invoice_number"), "currency_id": cad.id, @@ -245,10 +249,17 @@ class FusionBillingInvoiceLedgerWizard(models.TransientModel): if not service: service = self.env["fusion.billing.service"].create( {"name": "NexaCloud", "code": "nexacloud"}) + company = (inv.get("partner_company") or "").strip() + name = company or inv.get("partner_name") or str(inv.get("user_external_id")) link = self.env["fusion.billing.account.link"]._resolve_or_create_partner( - service, str(inv.get("user_external_id")), - name=inv.get("partner_name"), email=inv.get("partner_email")) - return link.partner_id + service, str(inv.get("user_external_id")), name=name, email=inv.get("partner_email")) + partner = link.partner_id + # Name the partner for the BUSINESS (company), not the NexaCloud user's full_name — + # one person (e.g. "Gurpreet Singh") can manage several distinct customer businesses. + # Rewrite an existing partner so earlier full_name-based names get corrected. + if company and (partner.name != company or not partner.is_company): + partner.write({"name": company, "is_company": True}) + return partner @api.model def _fc_stripe_journal(self):