841df8d49f
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
242 lines
20 KiB
Python
242 lines
20 KiB
Python
import frappe
|
|
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
|
|
|
|
FIELDS = [
|
|
# Module 2 — commercial/product linkage
|
|
("Item", "makap_requires_packaging_specification", "Requires Packaging Specification", "Check", None, 0),
|
|
("Quotation Item", "makap_packaging_specification", "Packaging Specification", "Link", "Packaging Specification", 0),
|
|
("Quotation Item", "makap_specification_revision", "Packaging Specification Revision", "Data", None, 1),
|
|
("Quotation Item", "makap_technical_summary", "Technical Summary", "Small Text", None, 1),
|
|
("Sales Order Item", "makap_packaging_specification", "Packaging Specification", "Link", "Packaging Specification", 0),
|
|
("Sales Order Item", "makap_specification_revision", "Packaging Specification Revision", "Data", None, 1),
|
|
("Sales Order", "makap_customer_acceptance_basis", "Customer Acceptance Basis", "Select", "Customer PO\nSigned Quotation\nEmail/Written Confirmation\nDirect Confirmed Order\nOther", 0),
|
|
("Sales Order", "makap_acceptance_reference", "Acceptance Reference", "Data", None, 0),
|
|
# Module 4 — manufacturing readiness and process classification
|
|
("Operation", "makap_process_type", "Makap Process Type", "Select", "Printing — Gravure\nPrinting — Flexographic\nLamination\nSlitting\nRewinding\nConversion / Bag Making\nFinishing\nOther", 0),
|
|
("Work Order", "makap_release_section", "Release Readiness", "Section Break", None, 0),
|
|
("Work Order", "makap_packaging_specification", "Packaging Specification", "Link", "Packaging Specification", 1),
|
|
("Work Order", "makap_specification_revision", "Specification Revision", "Data", None, 1),
|
|
("Work Order", "makap_sales_order_item_reference", "Sales Order Item Reference", "Data", None, 0),
|
|
("Work Order", "makap_customer", "Customer", "Link", "Customer", 1),
|
|
("Work Order", "makap_manufacturing_readiness_status", "Manufacturing Readiness Status", "Select", "Not Checked\nBlocked\nReady\nReleased", 0),
|
|
("Work Order", "makap_manufacturing_readiness_blockers", "Manufacturing Readiness Blockers", "Small Text", None, 0),
|
|
("Work Order", "makap_readiness_checked_by", "Readiness Checked By", "Link", "User", 1),
|
|
("Work Order", "makap_readiness_checked_on", "Readiness Checked On", "Datetime", None, 1),
|
|
("Work Order", "makap_bom_compatibility_status", "BOM Compatibility Status", "Select", "Not Checked\nCompatible\nIncompatible\nNot Applicable", 0),
|
|
("Work Order", "makap_bom_compatibility_confirmed_by", "BOM Compatibility Confirmed By", "Link", "User", 1),
|
|
("Work Order", "makap_bom_compatibility_confirmed_on", "BOM Compatibility Confirmed On", "Datetime", None, 1),
|
|
("Work Order", "makap_process_compatibility_status", "Process Compatibility Status", "Select", "Not Checked\nCompatible\nIncompatible\nNot Applicable", 0),
|
|
("Work Order", "makap_tooling_readiness_status", "Tooling Readiness Status", "Select", "Not Checked\nReady\nBlocked\nNot Required", 1),
|
|
# Module 5 — bounded material accountability controls
|
|
("Item", "makap_material_control_class", "Material Control Class", "Select", "Standard\nControlled\nHigh Traceability", 0),
|
|
("Work Order", "makap_material_section", "Material Accountability", "Section Break", None, 0),
|
|
("Work Order", "makap_material_availability_status", "Material Availability Status", "Select", "Not Checked\nBlocked\nPartially Available\nAvailable", 0),
|
|
("Work Order", "makap_material_availability_checked_by", "Material Availability Checked By", "Link", "User", 1),
|
|
("Work Order", "makap_material_availability_checked_on", "Material Availability Checked On", "Datetime", None, 1),
|
|
("Work Order", "makap_material_availability_blocker_summary", "Material Availability Blocker Summary", "Small Text", None, 0),
|
|
("Work Order", "makap_material_close_status", "Material Close Status", "Select", "Open\nClosed\nException", 0),
|
|
("Work Order", "makap_material_close_checked_by", "Material Close Checked By", "Link", "User", 1),
|
|
("Work Order", "makap_material_close_checked_on", "Material Close Checked On", "Datetime", None, 1),
|
|
("Work Order", "makap_material_close_exception_summary", "Material Close Exception Summary", "Small Text", None, 0),
|
|
# Module 6 — quality eligibility and traceability context
|
|
("Quality Inspection", "makap_packaging_specification", "Packaging Specification", "Link", "Packaging Specification", 0),
|
|
("Quality Inspection", "makap_specification_revision", "Specification Revision", "Data", None, 1),
|
|
("Quality Inspection", "makap_work_order", "Work Order", "Link", "Work Order", 0),
|
|
("Quality Inspection", "makap_quality_gate_stage", "Quality Gate Stage", "Select", "First-Off\nIn-Process\nFinal\nIncoming\nOutgoing\nNot Applicable", 0),
|
|
("Quality Inspection", "makap_inspection_trigger", "Inspection Trigger", "Select", "Routine\nFirst-Off\nPeriodic\nPost-Adjustment\nPost-Rework\nComplaint\nRetrospective Hold", 0),
|
|
("Quality Inspection", "makap_eligibility_section", "Eligibility and Disposition", "Section Break", None, 0),
|
|
("Quality Inspection", "makap_quality_eligibility", "Quality Eligibility", "Select", "Not Determined\nEligible\nIneligible\nHold", 0),
|
|
("Quality Inspection", "makap_inspection_population_quantity", "Inspection Population Quantity", "Float", None, 0),
|
|
("Quality Inspection", "makap_population_uom", "Population UOM", "Link", "UOM", 0),
|
|
("Quality Inspection", "makap_sampling_unit", "Sampling Unit", "Data", None, 0),
|
|
("Quality Inspection", "makap_quality_disposition", "Quality Disposition", "Select", "Pending\nRelease\nHold\nReject\nRework\nScrap\nConcession", 0),
|
|
("Quality Inspection", "makap_disposition_quantity", "Disposition Quantity", "Float", None, 0),
|
|
("Quality Inspection", "makap_disposition_uom", "Disposition UOM", "Link", "UOM", 0),
|
|
("Quality Inspection", "makap_disposition_by", "Disposition By", "Link", "User", 1),
|
|
("Quality Inspection", "makap_disposition_on", "Disposition On", "Datetime", None, 1),
|
|
("Quality Inspection", "makap_disposition_reason", "Disposition Reason", "Small Text", None, 0),
|
|
("Quality Inspection", "makap_defect_failure_category", "Defect / Failure Category", "Data", None, 0),
|
|
("Quality Inspection", "makap_exception_section", "Exceptions, Reinspection and Concessions", "Section Break", None, 0),
|
|
("Quality Inspection", "makap_quality_exception_type", "Quality Exception Type", "Select", "None\nConcession\nReinspection\nRetrospective Hold\nTraceability Exception", 0),
|
|
("Quality Inspection", "makap_reinspection_of", "Reinspection Of", "Link", "Quality Inspection", 0),
|
|
("Quality Inspection", "makap_reinspection_type", "Reinspection Type", "Select", "Full\nPartial\nTargeted", 0),
|
|
("Quality Inspection", "makap_concession_scope", "Concession Scope", "Small Text", None, 0),
|
|
("Quality Inspection", "makap_concession_quantity", "Concession Quantity", "Float", None, 0),
|
|
("Quality Inspection", "makap_concession_uom", "Concession UOM", "Link", "UOM", 0),
|
|
("Quality Inspection", "makap_concession_reference", "Concession Reference", "Data", None, 0),
|
|
("Quality Inspection", "makap_customer_approval_required", "Customer Approval Required", "Check", None, 0),
|
|
("Quality Inspection", "makap_customer_approval_reference", "Customer Approval Reference", "Data", None, 0),
|
|
("Quality Inspection", "makap_inspection_equipment_reference", "Inspection Equipment Reference", "Data", None, 0),
|
|
("Quality Inspection", "makap_released_not_before", "Released Not Before", "Datetime", None, 0),
|
|
# Module 7 — planning, supply assurance and procurement evidence
|
|
("Production Plan Item", "makap_material_need_date", "Material Need Date", "Date", None, 0),
|
|
("Production Plan Item", "makap_coverage_mode", "Coverage Mode", "Select", "Pegged\nPooled", 0),
|
|
("Material Request Item", "makap_material_need_date", "Material Need Date", "Date", None, 0),
|
|
("Material Request Item", "makap_coverage_mode", "Coverage Mode", "Select", "Pegged\nPooled", 0),
|
|
("Purchase Order Item", "makap_sourcing_basis", "Sourcing Basis", "Select", "Approved Supplier\nCompetitive RFQ\nSingle Source\nEmergency\nOther", 0),
|
|
("Purchase Order Item", "makap_supplier_selection_reason", "Supplier Selection Reason", "Small Text", None, 0),
|
|
("Purchase Order Item", "makap_supplier_confirmation_status", "Supplier Confirmation Status", "Select", "Not Requested\nRequested\nConfirmed\nAt Risk\nDeclined", 0),
|
|
("Purchase Order Item", "makap_supplier_confirmation_reference", "Supplier Confirmation Reference", "Data", None, 0),
|
|
("Purchase Order Item", "makap_expected_eligible_date", "Expected Eligible Date", "Date", None, 0),
|
|
("Purchase Receipt Item", "makap_receipt_exception_type", "Receipt Exception Type", "Select", "None\nShort Receipt\nOver Receipt\nDamaged\nQuality Hold\nRejected\nDocumentation Exception", 0),
|
|
# Module 8A — dispatch and fulfilment control (Delivery Note header)
|
|
("Delivery Note", "makap_dispatch_gate_status", "Dispatch Gate Status", "Select", "Not Assessed\nBlocked\nReady", 1),
|
|
("Delivery Note", "makap_dispatch_gate_blockers", "Dispatch Gate Blockers", "Small Text", None, 1),
|
|
("Delivery Note", "makap_dispatch_gate_checked_by", "Dispatch Gate Checked By", "Link", "User", 1),
|
|
("Delivery Note", "makap_dispatch_gate_checked_on", "Dispatch Gate Checked On", "Datetime", None, 1),
|
|
("Delivery Note", "makap_dispatch_material_accountability_status", "Dispatch Material Accountability", "Select", "Not Assessed\nCleared\nException", 0),
|
|
("Delivery Note", "makap_dispatch_accountability_notes", "Dispatch Accountability Notes", "Small Text", None, 0),
|
|
# Placeholder only — real deposit/credit logic lands in Module 8B. Defaults to Not Required
|
|
# so the Dispatch Gate can already evaluate it without blocking every delivery today.
|
|
("Delivery Note", "makap_release_section", "Financial Release and Short-Close", "Section Break", None, 0),
|
|
("Delivery Note", "makap_financial_release_status", "Financial Release Status", "Select", "Not Required\nNot Assessed\nBlocked\nCleared\nException Approved", 0),
|
|
("Delivery Note", "makap_short_close", "Short-Close", "Check", None, 0),
|
|
("Delivery Note", "makap_short_close_reason", "Short-Close Reason", "Small Text", None, 0),
|
|
("Delivery Note", "makap_short_close_approved_by", "Short-Close Approved By", "Link", "User", 1),
|
|
("Delivery Note", "makap_over_delivery_approved_by", "Over-Delivery Approved By", "Link", "User", 1),
|
|
# Module 8A — dispatch and fulfilment control (Delivery Note Item)
|
|
("Delivery Note Item", "makap_packaging_specification", "Packaging Specification", "Link", "Packaging Specification", 1),
|
|
("Delivery Note Item", "makap_specification_revision", "Specification Revision", "Data", None, 1),
|
|
("Delivery Note Item", "makap_sales_order_item_reference", "Sales Order Item Reference", "Data", None, 1),
|
|
# No default on purpose — Reserved vs Pooled is a per-line decision the preparer must make
|
|
# explicitly until Makap sets a global default policy (see M8-OD-04, still open).
|
|
("Delivery Note Item", "makap_dispatch_allocation_mode", "Dispatch Allocation Mode", "Select", "\nReserved\nPooled", 0),
|
|
("Delivery Note Item", "makap_stock_reservation_entry", "Stock Reservation Entry", "Link", "Stock Reservation Entry", 0),
|
|
("Delivery Note Item", "makap_work_order", "Work Order", "Link", "Work Order", 0),
|
|
("Delivery Note Item", "makap_customer_trace_key", "Customer Trace Key", "Data", None, 0),
|
|
# Module 8B — financial release, deposit/credit and commercial billing
|
|
("Customer", "makap_credit_exposure_status", "Credit Exposure Status", "Select", "Not Assessed\nWithin Limit\nOver Limit\nFrozen", 0),
|
|
("Customer", "makap_financial_hold", "Financial Hold", "Check", None, 0),
|
|
("Customer", "makap_financial_hold_reason", "Financial Hold Reason", "Small Text", None, 0),
|
|
("Sales Order", "makap_deposit_required", "Deposit Required", "Check", None, 0),
|
|
("Sales Order", "makap_deposit_required_amount", "Deposit Required Amount", "Currency", None, 0),
|
|
# Derived on Sales Invoice/Payment Entry submit-cancel — see _recompute_billing_and_collection.
|
|
("Sales Order", "makap_billing_status", "Billing Status", "Select", "Not Billable\nUnbilled\nPartially Billed\nCommercially Billed", 1),
|
|
("Sales Order", "makap_delivered_unbilled", "Delivered-Unbilled Exposure", "Check", None, 1),
|
|
("Sales Order", "makap_collection_status", "Collection Status", "Select", "Not Yet Due\nDue\nPartially Paid\nPaid", 1),
|
|
("Payment Entry", "makap_payment_purpose", "Payment Purpose", "Select", "Deposit\nProgress Payment\nFinal Settlement\nOther", 0),
|
|
# Module 8B/8C — commercial billing and Uganda EFRIS fiscalisation (Sales Invoice)
|
|
("Sales Invoice", "makap_delivery_note_reference", "Delivery Note Reference", "Data", None, 1),
|
|
# Not Required is the default so non-EFRIS transactions aren't blocked before Makap confirms
|
|
# the integration method (M8-OD-01/02, still open).
|
|
("Sales Invoice", "makap_fiscalisation_status", "Fiscalisation Status", "Select", "Not Required\nPending\nFiscalised\nFailed", 0),
|
|
("Sales Invoice", "makap_fiscal_document_number", "Fiscal Document Number", "Data", None, 0),
|
|
("Sales Invoice", "makap_fiscal_verification_code", "Fiscal Verification/Reference Code", "Data", None, 0),
|
|
("Sales Invoice", "makap_fiscalisation_timestamp", "Fiscalisation Timestamp", "Datetime", None, 0),
|
|
("Sales Invoice", "makap_original_fiscal_document_reference", "Original Fiscal Document Reference", "Data", None, 0),
|
|
("Sales Invoice Item", "makap_delivery_note_item_reference", "Delivery Note Item Reference", "Data", None, 1),
|
|
# Module 8D — customer returns and replacement
|
|
("Warehouse", "makap_is_returns_hold_warehouse", "Is Returns Hold Warehouse", "Check", None, 0),
|
|
("Delivery Note Item", "makap_return_reason", "Return Reason", "Select", "\nQuality Defect\nWrong Item\nDamaged In Transit\nCustomer Changed Mind\nOver-Delivery Correction\nOther", 0),
|
|
("Delivery Note Item", "makap_return_quality_disposition", "Return Quality Disposition", "Select", "\nPending\nRelease\nReject\nRework\nScrap\nConcession", 0),
|
|
# Module 8E — supplier financial reconciliation
|
|
("Purchase Invoice", "makap_supplier_invoice_match_status", "Supplier Invoice Match Status", "Select", "Not Assessed\nMatched\nQuantity Variance\nRate Variance\nBoth Variance\nDisputed\nResolved", 0),
|
|
("Purchase Invoice", "makap_match_notes", "Match Notes", "Small Text", None, 0),
|
|
("Purchase Invoice Item", "makap_quantity_variance", "Quantity Variance", "Float", None, 1),
|
|
("Purchase Invoice Item", "makap_rate_variance", "Rate Variance", "Float", None, 1),
|
|
# Module 8F — manufacturing execution cost interpretation
|
|
("Work Order", "makap_cost_section", "Cost Interpretation", "Section Break", None, 0),
|
|
("Work Order", "makap_cost_status", "Cost Status", "Select", "Not Assessed\nProvisional\nReady to Close\nClosed\nRestatement Required", 0),
|
|
("Work Order", "makap_cost_confidence", "Cost Confidence", "Select", "Not Assessed\nHigh\nQualified\nLow / Provisional", 0),
|
|
("Work Order", "makap_cost_attribution_basis", "Cost Attribution Basis", "Select", "Not Assessed\nExact\nDirectly Derived\nPooled / Valuation-Based\nQualified", 0),
|
|
# Roll tracking — every film/roll batch carries its physical dimensions (PRD 4, B3)
|
|
("Batch", "makap_roll_stage", "Roll Stage", "Select", "\nResin / Raw Material\nBase Film\nPrinted Roll\nLaminated Roll\nSlit Reel\nFinished Goods", 0),
|
|
("Batch", "makap_width_mm", "Width (mm)", "Float", None, 0),
|
|
("Batch", "makap_length_m", "Length (m)", "Float", None, 0),
|
|
("Batch", "makap_micron", "Thickness (micron)", "Float", None, 0),
|
|
("Batch", "makap_roll_col", None, "Column Break", None, 0),
|
|
("Batch", "makap_net_weight_kg", "Net Weight (kg)", "Float", None, 0),
|
|
("Batch", "makap_gross_weight_kg", "Gross Weight (kg)", "Float", None, 0),
|
|
("Batch", "makap_supplier_lot", "Supplier Lot / Roll No", "Data", None, 0),
|
|
("Batch", "makap_workstation", "Produced On", "Link", "Workstation", 0),
|
|
("Batch", "makap_work_order", "Work Order", "Link", "Work Order", 0),
|
|
# Operator capture on each stage's Job Card (PRD 4 stage 3, problem 4)
|
|
("Job Card", "makap_output_rolls", "Output Rolls", "Int", None, 0),
|
|
("Job Card", "makap_output_length_m", "Output Length (m)", "Float", None, 0),
|
|
("Job Card", "makap_output_weight_kg", "Output Weight (kg)", "Float", None, 0),
|
|
("Job Card", "makap_output_col", None, "Column Break", None, 0),
|
|
("Job Card", "makap_setup_waste_kg", "Set-up Waste (kg)", "Float", None, 0),
|
|
("Job Card", "makap_running_waste_kg", "Running Waste (kg)", "Float", None, 0),
|
|
("Job Card", "makap_waste_pct", "Waste %", "Percent", None, 1),
|
|
("Job Card", "makap_waste_reason", "Main Waste Reason", "Select", "\nSet-up / Make-ready\nRegistration\nColour Match\nWeb Break\nWrinkles / Creasing\nBond Failure\nSeal Failure\nEdge Trim\nMaterial Defect\nMachine Fault\nOther", 0),
|
|
("Job Card", "makap_waste_notes", "Waste Notes", "Small Text", None, 0),
|
|
# Quotation cost estimate from an estimating BOM (PRD 4 stage 1, M4)
|
|
("Quotation Item", "makap_estimate_bom", "Estimating BOM", "Link", "BOM", 0),
|
|
("Quotation Item", "makap_waste_pct", "Expected Waste %", "Percent", None, 0),
|
|
("Quotation Item", "makap_setup_cost", "Set-up Cost", "Currency", None, 0),
|
|
("Quotation Item", "makap_cylinder_charge", "Cylinder / Plate Charge", "Currency", None, 0),
|
|
("Quotation Item", "makap_estimate_col", None, "Column Break", None, 0),
|
|
("Quotation Item", "makap_material_cost", "Estimated Material Cost", "Currency", None, 1),
|
|
("Quotation Item", "makap_conversion_cost", "Estimated Conversion Cost", "Currency", None, 1),
|
|
("Quotation Item", "makap_estimated_cost", "Estimated Total Cost", "Currency", None, 1),
|
|
("Quotation Item", "makap_estimated_margin_pct", "Estimated Margin %", "Percent", None, 1),
|
|
]
|
|
|
|
|
|
# Quality decisions: only roles granted permlevel 1 on Quality Inspection
|
|
# (QA, see setup_roles) can change these.
|
|
QUALITY_DECISION_FIELDS = {
|
|
"makap_quality_eligibility", "makap_quality_disposition", "makap_disposition_quantity",
|
|
"makap_disposition_uom", "makap_disposition_reason", "makap_concession_scope",
|
|
"makap_concession_quantity", "makap_concession_uom", "makap_concession_reference",
|
|
"makap_released_not_before",
|
|
}
|
|
|
|
# Where each DocType's Makap fields live. Parent forms get one "Makap" tab;
|
|
# child rows get a section. Operator and roll fields sit in the main view
|
|
# because they are filled in on the shop floor, not reviewed later.
|
|
CONTAINERS = {
|
|
"Job Card": ("Section Break", "Output and Waste", "actual_end_date"),
|
|
"Batch": ("Section Break", "Roll Details", None),
|
|
}
|
|
|
|
|
|
def _end_anchor(meta):
|
|
"""Last standard field, keeping a trailing Connections tab last."""
|
|
standard = [df for df in meta.fields if not df.get("is_custom_field")]
|
|
if len(standard) > 1 and standard[-1].fieldtype == "Tab Break":
|
|
return standard[-2].fieldname
|
|
return standard[-1].fieldname
|
|
|
|
|
|
def build_custom_fields():
|
|
grouped = {}
|
|
for dt, fieldname, label, fieldtype, options, read_only in FIELDS:
|
|
field = {"fieldname": fieldname, "label": label, "fieldtype": fieldtype, "options": options, "read_only": read_only}
|
|
if dt == "Quality Inspection" and fieldname in QUALITY_DECISION_FIELDS:
|
|
field["permlevel"] = 1
|
|
grouped.setdefault(dt, []).append(field)
|
|
|
|
result = {}
|
|
for dt, fields in grouped.items():
|
|
if not frappe.db.exists("DocType", dt):
|
|
continue
|
|
meta = frappe.get_meta(dt)
|
|
if dt in CONTAINERS:
|
|
break_type, label, anchor = CONTAINERS[dt]
|
|
elif meta.istable:
|
|
break_type, label, anchor = "Section Break", "Makap", None
|
|
else:
|
|
break_type, label, anchor = "Tab Break", "Makap", None
|
|
|
|
chain = [
|
|
{
|
|
"fieldname": "makap_tab" if break_type == "Tab Break" else "makap_section",
|
|
"label": label,
|
|
"fieldtype": break_type,
|
|
"insert_after": anchor or _end_anchor(meta),
|
|
}
|
|
]
|
|
for field in fields:
|
|
field["insert_after"] = chain[-1]["fieldname"]
|
|
chain.append(field)
|
|
result[dt] = chain
|
|
return result
|
|
|
|
|
|
def ensure_custom_fields():
|
|
"""Create or update every Makap custom field, including its position on the form."""
|
|
fields = build_custom_fields()
|
|
create_custom_fields(fields, update=True)
|
|
return sorted(f"{dt}.{f['fieldname']}" for dt, rows in fields.items() for f in rows)
|