Compare commits
3 Commits
449f29fc7f
...
7a02382623
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a02382623 | ||
|
|
169e97af02 | ||
|
|
3c959771ae |
@@ -3,7 +3,7 @@
|
|||||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||||
{
|
{
|
||||||
'name': 'Fusion Plating — Native Jobs',
|
'name': 'Fusion Plating — Native Jobs',
|
||||||
'version': '19.0.8.22.9',
|
'version': '19.0.8.22.10',
|
||||||
'category': 'Manufacturing/Plating',
|
'category': 'Manufacturing/Plating',
|
||||||
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
'summary': 'Native plating job model — replaces mrp.production / mrp.workorder bridge.',
|
||||||
'author': 'Nexa Systems Inc.',
|
'author': 'Nexa Systems Inc.',
|
||||||
|
|||||||
@@ -10,7 +10,15 @@ from odoo import api, models
|
|||||||
|
|
||||||
|
|
||||||
class ReportFpJobMargin(models.AbstractModel):
|
class ReportFpJobMargin(models.AbstractModel):
|
||||||
_name = 'report.fusion_plating_jobs.report_fp_job_margin'
|
# Odoo looks up the report's data model via report.<report_name>.
|
||||||
|
# The action's report_name is `fusion_plating_jobs.report_fp_job_margin_template`,
|
||||||
|
# so this MUST be `report.fusion_plating_jobs.report_fp_job_margin_template`.
|
||||||
|
# Pre-2026-05-12 the model name was missing the `_template` suffix,
|
||||||
|
# which silently caused _get_report_values to never fire and the
|
||||||
|
# template rendered with no `rows` -> blank PDF. The t-field error
|
||||||
|
# was masking this because it crashed earlier; once t-field was
|
||||||
|
# swapped to t-esc the blank-render surfaced.
|
||||||
|
_name = 'report.fusion_plating_jobs.report_fp_job_margin_template'
|
||||||
_description = 'Work Order Margin Report'
|
_description = 'Work Order Margin Report'
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
from . import models
|
from . import models
|
||||||
from .hooks import post_init_hook
|
from .hooks import pre_init_hook, post_init_hook
|
||||||
|
|||||||
@@ -31,6 +31,7 @@
|
|||||||
"data/09_res_partner.xml",
|
"data/09_res_partner.xml",
|
||||||
"data/10_account_reconcile_model.xml",
|
"data/10_account_reconcile_model.xml",
|
||||||
],
|
],
|
||||||
|
"pre_init_hook": "pre_init_hook",
|
||||||
"post_init_hook": "post_init_hook",
|
"post_init_hook": "post_init_hook",
|
||||||
"installable": True,
|
"installable": True,
|
||||||
"application": False,
|
"application": False,
|
||||||
|
|||||||
@@ -1,5 +1,24 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="0">
|
<data noupdate="0">
|
||||||
|
|
||||||
|
<!-- Named 'Customer Project' to avoid collision with the project module's
|
||||||
|
auto-created 'Project' plan. This is where customer-engagement
|
||||||
|
analytic accounts (PRJ-YYYY-CUST-NAME) live. -->
|
||||||
|
<record id="plan_project" model="account.analytic.plan">
|
||||||
|
<field name="name">Customer Project</field>
|
||||||
|
<field name="default_applicability">mandatory</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="plan_department" model="account.analytic.plan">
|
||||||
|
<field name="name">Department</field>
|
||||||
|
<field name="default_applicability">mandatory</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="plan_sred_tag" model="account.analytic.plan">
|
||||||
|
<field name="name">SR&ED Tag</field>
|
||||||
|
<field name="default_applicability">optional</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -1,5 +1,74 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<odoo>
|
<odoo>
|
||||||
<data noupdate="0">
|
<data noupdate="0">
|
||||||
|
|
||||||
|
<!-- Department analytic accounts -->
|
||||||
|
<record id="aa_dept_dev" model="account.analytic.account">
|
||||||
|
<field name="name">Development</field>
|
||||||
|
<field name="code">DEPT-DEV</field>
|
||||||
|
<field name="plan_id" ref="plan_department"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_dept_sales" model="account.analytic.account">
|
||||||
|
<field name="name">Sales & Marketing</field>
|
||||||
|
<field name="code">DEPT-SALES</field>
|
||||||
|
<field name="plan_id" ref="plan_department"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_dept_admin" model="account.analytic.account">
|
||||||
|
<field name="name">Admin & Operations</field>
|
||||||
|
<field name="code">DEPT-ADMIN</field>
|
||||||
|
<field name="plan_id" ref="plan_department"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_dept_hosting" model="account.analytic.account">
|
||||||
|
<field name="name">Hosting Operations</field>
|
||||||
|
<field name="code">DEPT-HOSTING</field>
|
||||||
|
<field name="plan_id" ref="plan_department"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<!-- SR&ED Tag analytic accounts -->
|
||||||
|
<record id="aa_sred_t4_dev" model="account.analytic.account">
|
||||||
|
<field name="name">T4 Dev Salary — full proxy</field>
|
||||||
|
<field name="code">SRED-T4-DEV-SALARY</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_sred_specified" model="account.analytic.account">
|
||||||
|
<field name="name">Specified Employee Salary — 75% cap</field>
|
||||||
|
<field name="code">SRED-SPECIFIED-EMPLOYEE</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_sred_contr_ca_arm" model="account.analytic.account">
|
||||||
|
<field name="name">Contractor CA Arm's Length — 80% eligible</field>
|
||||||
|
<field name="code">SRED-CONTRACTOR-CA-ARM-LENGTH</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_sred_contr_ca_naf" model="account.analytic.account">
|
||||||
|
<field name="name">Contractor CA Non-Arm's Length</field>
|
||||||
|
<field name="code">SRED-CONTRACTOR-CA-NON-ARM-LENGTH</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_sred_materials" model="account.analytic.account">
|
||||||
|
<field name="name">Materials Consumed in R&D</field>
|
||||||
|
<field name="code">SRED-MATERIALS-CONSUMED</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_sred_overhead_basis" model="account.analytic.account">
|
||||||
|
<field name="name">Overhead Proxy Basis (direct labour basis)</field>
|
||||||
|
<field name="code">SRED-OVERHEAD-PROXY-BASIS</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="aa_sred_not_eligible" model="account.analytic.account">
|
||||||
|
<field name="name">Not Eligible (default)</field>
|
||||||
|
<field name="code">NOT-ELIGIBLE</field>
|
||||||
|
<field name="plan_id" ref="plan_sred_tag"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
</data>
|
</data>
|
||||||
</odoo>
|
</odoo>
|
||||||
|
|||||||
@@ -4,6 +4,72 @@ import logging
|
|||||||
_logger = logging.getLogger(__name__)
|
_logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
# l10n_ca account codes that collide with the Nexa CoA design and that
|
||||||
|
# l10n_ca pre-loads with 'income_other'/'expense'/etc. types we don't want.
|
||||||
|
# Each of these is checked at pre_init: if it has zero postings we suffix
|
||||||
|
# its code with '.OLD' and archive it so our XML can claim the code.
|
||||||
|
# Codes with postings are LEFT ALONE — we renumbered the Nexa code instead
|
||||||
|
# (115100 stays as l10n_ca 'Customers Account' AR; Nexa shareholder receivable
|
||||||
|
# moved to 119100. 511100 stays as l10n_ca 'Inside Purchases'; Nexa Cloud
|
||||||
|
# Infrastructure moved to 511105).
|
||||||
|
_L10N_CA_COLLISION_CODES = [
|
||||||
|
"118100", "118200", "118300",
|
||||||
|
"213100", "214100",
|
||||||
|
"221200",
|
||||||
|
"311100", "311200", "311300",
|
||||||
|
"411100", "411200", "411300",
|
||||||
|
"413100", "413200", "413300",
|
||||||
|
"511110", "511120", "511130", "511140", "511200", "511210",
|
||||||
|
"512100", "512110", "512200",
|
||||||
|
"611100", "611200", "611300",
|
||||||
|
"612100", "612200",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def pre_init_hook(env):
|
||||||
|
"""Run BEFORE XML data is loaded. Clear l10n_ca account codes that would
|
||||||
|
collide with Nexa's chart of accounts."""
|
||||||
|
_logger.info("nexa_coa_setup: pre_init_hook starting")
|
||||||
|
_clear_l10n_ca_collisions(env)
|
||||||
|
_logger.info("nexa_coa_setup: pre_init_hook complete")
|
||||||
|
|
||||||
|
|
||||||
|
def _clear_l10n_ca_collisions(env):
|
||||||
|
"""For each colliding code: if it has zero postings, rename to NNNNNN.OLD
|
||||||
|
and set inactive. If it has postings, leave alone (Nexa code was renumbered
|
||||||
|
in the XML to avoid the conflict)."""
|
||||||
|
cleared = 0
|
||||||
|
kept_with_postings = 0
|
||||||
|
not_found = 0
|
||||||
|
for code in _L10N_CA_COLLISION_CODES:
|
||||||
|
acc = env["account.account"].search([("code", "=", code)], limit=1)
|
||||||
|
if not acc:
|
||||||
|
not_found += 1
|
||||||
|
continue
|
||||||
|
usage = env["account.move.line"].search_count([("account_id", "=", acc.id)])
|
||||||
|
if usage > 0:
|
||||||
|
_logger.info(
|
||||||
|
"nexa_coa_setup: keeping l10n_ca account %s (%s) — %d postings exist",
|
||||||
|
code, acc.name, usage,
|
||||||
|
)
|
||||||
|
kept_with_postings += 1
|
||||||
|
continue
|
||||||
|
new_code = f"{code}.OLD"
|
||||||
|
# Skip if already suffixed (idempotency)
|
||||||
|
if acc.code.endswith(".OLD"):
|
||||||
|
continue
|
||||||
|
acc.write({
|
||||||
|
"code": new_code,
|
||||||
|
"name": f"(l10n_ca LEGACY) {acc.name or acc.display_name}",
|
||||||
|
"active": False,
|
||||||
|
})
|
||||||
|
cleared += 1
|
||||||
|
_logger.info(
|
||||||
|
"nexa_coa_setup: collision sweep — cleared %d, kept-with-postings %d, not-found %d",
|
||||||
|
cleared, kept_with_postings, not_found,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def post_init_hook(env):
|
def post_init_hook(env):
|
||||||
"""Imperative one-shot operations after module data is loaded.
|
"""Imperative one-shot operations after module data is loaded.
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user