# Step 8 — Jane creates the invoice for the completed SO and posts it. # Test: # A) SO has invoice_status = 'to invoice' after delivery # B) Jane creates the invoice # C) Invoice draft has correct lines, taxes, payment terms # D) Jane posts → invoice posted, account moves balanced # E) Notification fires (best-effort) so = env['sale.order'].browse(423) print(f'[Jane] Looking at SO {so.name}') print(f' state: {so.state}') print(f' invoice_status: {so.invoice_status}') print(f' amount_total: {so.amount_total} {so.currency_id.symbol}') print(f' payment_term_id: {so.payment_term_id.name if so.payment_term_id else None}') print(f' x_fc_invoice_strategy: {so.x_fc_invoice_strategy}') print(f' partner.account hold? {getattr(so.partner_id, "x_fc_account_hold", False)}') # What's already invoiced? existing = env['account.move'].search([ ('invoice_origin', '=', so.name), ]) print(f' Existing invoices for this SO: {len(existing)}') for inv in existing: print(f' {inv.name}: state={inv.state}, type={inv.move_type}, amount={inv.amount_total}') # Path A: create invoices. print() print('[Jane] Clicks "Create Invoice"') if so.invoice_status == 'to invoice': try: result = so._create_invoices() new_invs = env['account.move'].search([ ('invoice_origin', '=', so.name), ('id', 'not in', existing.ids), ]) print(f' Created {len(new_invs)} new invoice(s)') for inv in new_invs: print(f' {inv.name}: state={inv.state}, lines={len(inv.invoice_line_ids)}') for ln in inv.invoice_line_ids: print(f' - {ln.name[:50]}: qty={ln.quantity}, price={ln.price_unit}, subtotal={ln.price_subtotal}') except Exception as e: print(f' ❌ {e}') else: print(f' Skipped — invoice_status={so.invoice_status} (nothing to invoice)') new_invs = env['account.move'].browse() # Path B: post. if new_invs: inv = new_invs[0] print() print(f'[Jane] Posting invoice {inv.name}:') try: inv.action_post() print(f' ✓ state={inv.state}') print(f' payment_state={inv.payment_state}') except Exception as e: print(f' ❌ {e}') # Verify the SO progress: invoice_status should now show 'invoiced' print() print(f'[Jane] After posting:') print(f' SO invoice_status: {so.invoice_status}') print(f' Outstanding receivables on partner: {sum(env["account.move"].search([("partner_id", "=", so.partner_id.id), ("move_type", "=", "out_invoice"), ("state", "=", "posted"), ("payment_state", "in", ("not_paid", "partial"))]).mapped("amount_residual"))}') # Notification check. print() print(f'[Jane] Notification logs for this SO/invoice:') NotifLog = env['fp.notification.log'] if 'fp.notification.log' in env else None if NotifLog and new_invs: logs = NotifLog.search([('source_record_id', 'in', new_invs.ids)]) print(f' {len(logs)} notification log(s)') for lg in logs: print(f' {lg.trigger_event} → {lg.partner_id.name if lg.partner_id else "(no partner)"} sent_at={lg.sent_at if "sent_at" in lg._fields else "?"}') env.cr.commit() print() print('== Step 8 complete ==')