folder rename
This commit is contained in:
6
fusion_plating/fusion_plating_reports/models/__init__.py
Normal file
6
fusion_plating/fusion_plating_reports/models/__init__.py
Normal file
@@ -0,0 +1,6 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from . import report_wo_margin
|
||||
165
fusion_plating/fusion_plating_reports/models/report_wo_margin.py
Normal file
165
fusion_plating/fusion_plating_reports/models/report_wo_margin.py
Normal file
@@ -0,0 +1,165 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright 2026 Nexa Systems Inc.
|
||||
# License OPL-1 (Odoo Proprietary License v1.0)
|
||||
# Part of the Fusion Plating product family.
|
||||
|
||||
from odoo import api, models
|
||||
|
||||
|
||||
class ReportWoMargin(models.AbstractModel):
|
||||
"""Work Order Margin Report Data.
|
||||
|
||||
Computes cost, revenue, and margin breakdowns for manufacturing orders
|
||||
so the QWeb template can render a Steelhead-style margin report.
|
||||
"""
|
||||
_name = 'report.fusion_plating_reports.report_wo_margin'
|
||||
_description = 'WO Margin Report Data'
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# helpers
|
||||
# ------------------------------------------------------------------
|
||||
def _get_station_costs(self, mo):
|
||||
"""Return a list of dicts with per-station cost breakdown."""
|
||||
station_costs = []
|
||||
for wo in mo.workorder_ids:
|
||||
wc = wo.workcenter_id
|
||||
labour_rate = wc.costs_hour if wc else 0.0
|
||||
# Sum tracked time from productivity records (minutes)
|
||||
time_minutes = sum(wo.time_ids.mapped('duration'))
|
||||
time_hours = time_minutes / 60.0
|
||||
labour_cost = time_hours * labour_rate
|
||||
|
||||
# Operation cost uses the same rate & dwell for now
|
||||
operation_rate = labour_rate
|
||||
dwell_minutes = time_minutes
|
||||
dwell_hours = dwell_minutes / 60.0
|
||||
operation_cost = dwell_hours * operation_rate
|
||||
|
||||
total = labour_cost + operation_cost
|
||||
station_costs.append({
|
||||
'station': wc.name if wc else 'Unknown',
|
||||
'labour_rate': labour_rate,
|
||||
'labour_time': time_minutes,
|
||||
'labour_hours': time_hours,
|
||||
'labour_cost': labour_cost,
|
||||
'operation_rate': operation_rate,
|
||||
'dwell_time': dwell_minutes,
|
||||
'dwell_hours': dwell_hours,
|
||||
'operation_cost': operation_cost,
|
||||
'total_cost': total,
|
||||
})
|
||||
return station_costs
|
||||
|
||||
def _get_part_margins(self, mo, revenue):
|
||||
"""Return margin breakdown per unique product (part number)."""
|
||||
parts = {}
|
||||
for wo in mo.workorder_ids:
|
||||
product = wo.product_id or mo.product_id
|
||||
key = product.id
|
||||
if key not in parts:
|
||||
parts[key] = {
|
||||
'product': product,
|
||||
'part_number': product.default_code or product.name,
|
||||
'count': 0,
|
||||
'labour_cost': 0.0,
|
||||
'station_labour_cost': 0.0,
|
||||
'station_operation_cost': 0.0,
|
||||
'outsourcing_cost': 0.0,
|
||||
}
|
||||
entry = parts[key]
|
||||
entry['count'] += 1
|
||||
time_hours = sum(wo.time_ids.mapped('duration')) / 60.0
|
||||
rate = wo.workcenter_id.costs_hour if wo.workcenter_id else 0.0
|
||||
cost = time_hours * rate
|
||||
entry['labour_cost'] += cost
|
||||
entry['station_labour_cost'] += cost
|
||||
entry['station_operation_cost'] += cost
|
||||
|
||||
# Distribute revenue equally across parts for per-part metrics
|
||||
part_list = list(parts.values())
|
||||
total_parts = sum(p['count'] for p in part_list) or 1
|
||||
for p in part_list:
|
||||
p['so_total'] = revenue * (p['count'] / total_parts)
|
||||
p['so_per_part'] = p['so_total'] / p['count'] if p['count'] else 0
|
||||
p['unit_labour'] = p['labour_cost'] / p['count'] if p['count'] else 0
|
||||
total_cost = (
|
||||
p['labour_cost'] + p['station_labour_cost']
|
||||
+ p['station_operation_cost'] + p['outsourcing_cost']
|
||||
)
|
||||
p['margin_pct'] = (
|
||||
(p['so_total'] - total_cost) / p['so_total'] * 100
|
||||
) if p['so_total'] else 0
|
||||
return part_list
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Report entry point
|
||||
# ------------------------------------------------------------------
|
||||
@api.model
|
||||
def _get_report_values(self, docids, data=None):
|
||||
productions = self.env['mrp.production'].browse(docids)
|
||||
docs = []
|
||||
for mo in productions:
|
||||
# Revenue from linked sale order
|
||||
revenue = 0.0
|
||||
if hasattr(mo, 'sale_order_id') and mo.sale_order_id:
|
||||
revenue = sum(
|
||||
mo.sale_order_id.order_line.mapped('price_subtotal')
|
||||
)
|
||||
|
||||
# Station costs
|
||||
station_costs = self._get_station_costs(mo)
|
||||
part_labour_cost = sum(s['labour_cost'] for s in station_costs)
|
||||
station_labour_cost = part_labour_cost # same pool
|
||||
station_operation_cost = sum(
|
||||
s['operation_cost'] for s in station_costs
|
||||
)
|
||||
|
||||
# Inventory cost (raw materials)
|
||||
inventory_cost = sum(
|
||||
m.product_id.standard_price * m.quantity
|
||||
for m in mo.move_raw_ids
|
||||
)
|
||||
|
||||
# Outsourcing cost placeholder
|
||||
outsourcing_cost = 0.0
|
||||
|
||||
total_cost = (
|
||||
part_labour_cost + station_labour_cost
|
||||
+ station_operation_cost + inventory_cost
|
||||
+ outsourcing_cost
|
||||
)
|
||||
gross_profit = revenue - total_cost
|
||||
margin_pct = (
|
||||
(gross_profit / revenue * 100) if revenue else 0.0
|
||||
)
|
||||
|
||||
# Station cost percentages
|
||||
total_station_cost = sum(s['total_cost'] for s in station_costs)
|
||||
for s in station_costs:
|
||||
s['percentage'] = (
|
||||
(s['total_cost'] / total_station_cost * 100)
|
||||
if total_station_cost else 0
|
||||
)
|
||||
|
||||
# Part margins
|
||||
part_margins = self._get_part_margins(mo, revenue)
|
||||
|
||||
docs.append({
|
||||
'mo': mo,
|
||||
'revenue': revenue,
|
||||
'part_labour_cost': part_labour_cost,
|
||||
'station_labour_cost': station_labour_cost,
|
||||
'station_operation_cost': station_operation_cost,
|
||||
'inventory_cost': inventory_cost,
|
||||
'outsourcing_cost': outsourcing_cost,
|
||||
'total_cost': total_cost,
|
||||
'gross_profit': gross_profit,
|
||||
'margin_pct': margin_pct,
|
||||
'station_costs': station_costs,
|
||||
'part_margins': part_margins,
|
||||
})
|
||||
|
||||
return {
|
||||
'doc_ids': docids,
|
||||
'docs': docs,
|
||||
}
|
||||
Reference in New Issue
Block a user