diff --git a/makap_backend/makap_backend/doctype/packaging_specification/packaging_specification.json b/makap_backend/makap_backend/doctype/packaging_specification/packaging_specification.json index ba5f030..43ae6e6 100644 --- a/makap_backend/makap_backend/doctype/packaging_specification/packaging_specification.json +++ b/makap_backend/makap_backend/doctype/packaging_specification/packaging_specification.json @@ -403,8 +403,14 @@ "options": "Critical Acceptance Requirement" } ], - "links": [], - "modified": "2026-09-02 00:05:48.067955", + "links": [ + { + "group": "Tooling", + "link_doctype": "Print Cylinder", + "link_fieldname": "packaging_specification" + } + ], + "modified": "2026-10-09 00:00:00", "modified_by": "Administrator", "module": "makap_backend", "name": "Packaging Specification", @@ -476,4 +482,4 @@ "states": [], "title_field": "specification_series", "track_changes": 1 -} \ No newline at end of file +} diff --git a/makap_backend/makap_backend/doctype/print_cylinder/__init__.py b/makap_backend/makap_backend/doctype/print_cylinder/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/makap_backend/makap_backend/doctype/print_cylinder/print_cylinder.json b/makap_backend/makap_backend/doctype/print_cylinder/print_cylinder.json new file mode 100644 index 0000000..b0f6dd1 --- /dev/null +++ b/makap_backend/makap_backend/doctype/print_cylinder/print_cylinder.json @@ -0,0 +1,337 @@ +{ + "actions": [], + "autoname": "field:cylinder_id", + "creation": "2026-10-09 00:00:00", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "identity_section", + "cylinder_id", + "tooling_type", + "status", + "identity_col", + "packaging_specification", + "item", + "customer", + "colour_station", + "colour_name", + "dimensions_section", + "circumference_mm", + "face_width_mm", + "dimensions_col", + "repeat_length_mm", + "engraving_reference", + "ownership_section", + "ownership", + "engraver", + "ownership_col", + "purchase_cost", + "customer_charge", + "condition_section", + "location", + "condition", + "last_used_on", + "last_used_work_order", + "condition_col", + "impression_count", + "impression_limit", + "retirement_reason", + "notes" + ], + "fields": [ + { + "fieldname": "identity_section", + "fieldtype": "Section Break", + "label": "Identity" + }, + { + "fieldname": "cylinder_id", + "fieldtype": "Data", + "label": "Cylinder / Plate ID", + "reqd": 1, + "unique": 1, + "in_list_view": 1, + "description": "The number engraved or labelled on the cylinder, sleeve or plate." + }, + { + "fieldname": "tooling_type", + "fieldtype": "Select", + "label": "Tooling Type", + "options": "Gravure Cylinder\nFlexo Plate\nFlexo Sleeve\nAnilox Roll\nCutting Die", + "reqd": 1, + "in_list_view": 1, + "in_standard_filter": 1 + }, + { + "fieldname": "status", + "fieldtype": "Select", + "label": "Status", + "options": "Ordered\nIn Prepress\nReady\nIn Use\nUnder Repair\nRetired", + "default": "Ordered", + "reqd": 1, + "in_list_view": 1, + "in_standard_filter": 1 + }, + { + "fieldname": "identity_col", + "fieldtype": "Column Break" + }, + { + "fieldname": "packaging_specification", + "fieldtype": "Link", + "label": "Packaging Specification", + "options": "Packaging Specification", + "in_standard_filter": 1 + }, + { + "fieldname": "item", + "fieldtype": "Link", + "label": "Item", + "options": "Item", + "fetch_from": "packaging_specification.item", + "read_only": 1 + }, + { + "fieldname": "customer", + "fieldtype": "Link", + "label": "Customer", + "options": "Customer", + "fetch_from": "packaging_specification.customer", + "read_only": 1, + "in_standard_filter": 1 + }, + { + "fieldname": "colour_station", + "fieldtype": "Int", + "label": "Colour Station" + }, + { + "fieldname": "colour_name", + "fieldtype": "Data", + "label": "Colour", + "in_list_view": 1 + }, + { + "fieldname": "dimensions_section", + "fieldtype": "Section Break", + "label": "Dimensions" + }, + { + "fieldname": "circumference_mm", + "fieldtype": "Float", + "label": "Circumference (mm)" + }, + { + "fieldname": "face_width_mm", + "fieldtype": "Float", + "label": "Face Width (mm)" + }, + { + "fieldname": "dimensions_col", + "fieldtype": "Column Break" + }, + { + "fieldname": "repeat_length_mm", + "fieldtype": "Float", + "label": "Repeat Length (mm)" + }, + { + "fieldname": "engraving_reference", + "fieldtype": "Data", + "label": "Engraving / Screen Reference", + "description": "Screen ruling, cell depth or plate recipe reference." + }, + { + "fieldname": "ownership_section", + "fieldtype": "Section Break", + "label": "Ownership and Cost" + }, + { + "fieldname": "ownership", + "fieldtype": "Select", + "label": "Ownership", + "options": "Makap\nCustomer\nCharged to Customer", + "reqd": 1, + "default": "Makap" + }, + { + "fieldname": "engraver", + "fieldtype": "Link", + "label": "Engraver / Supplier", + "options": "Supplier" + }, + { + "fieldname": "ownership_col", + "fieldtype": "Column Break" + }, + { + "fieldname": "purchase_cost", + "fieldtype": "Currency", + "label": "Purchase Cost" + }, + { + "fieldname": "customer_charge", + "fieldtype": "Currency", + "label": "Charged to Customer", + "depends_on": "eval:doc.ownership=='Charged to Customer'" + }, + { + "fieldname": "condition_section", + "fieldtype": "Section Break", + "label": "Location and Condition" + }, + { + "fieldname": "location", + "fieldtype": "Data", + "label": "Rack / Location", + "in_list_view": 1 + }, + { + "fieldname": "condition", + "fieldtype": "Select", + "label": "Condition", + "options": "New\nGood\nWorn\nNeeds Re-chrome\nDamaged", + "default": "New" + }, + { + "fieldname": "last_used_on", + "fieldtype": "Date", + "label": "Last Used On" + }, + { + "fieldname": "last_used_work_order", + "fieldtype": "Link", + "label": "Last Used On Work Order", + "options": "Work Order" + }, + { + "fieldname": "condition_col", + "fieldtype": "Column Break" + }, + { + "fieldname": "impression_count", + "fieldtype": "Float", + "label": "Impressions to Date", + "default": "0" + }, + { + "fieldname": "impression_limit", + "fieldtype": "Float", + "label": "Impression Limit", + "description": "Re-chrome or replace at this count. Leave empty if not tracked." + }, + { + "fieldname": "retirement_reason", + "fieldtype": "Small Text", + "label": "Retirement Reason", + "depends_on": "eval:doc.status=='Retired'", + "mandatory_depends_on": "eval:doc.status=='Retired'" + }, + { + "fieldname": "notes", + "fieldtype": "Small Text", + "label": "Notes" + } + ], + "links": [], + "modified": "2026-10-09 00:00:00", + "modified_by": "Administrator", + "module": "makap_backend", + "name": "Print Cylinder", + "naming_rule": "By fieldname", + "owner": "Administrator", + "permissions": [ + { + "role": "System Manager", + "read": 1, + "write": 1, + "create": 1, + "delete": 1, + "print": 1, + "report": 1, + "export": 1, + "email": 1, + "share": 1 + }, + { + "role": "Manufacturing Manager", + "read": 1, + "write": 1, + "create": 1, + "delete": 0, + "print": 1, + "report": 1, + "export": 1, + "email": 1, + "share": 1 + }, + { + "role": "Technical Authority", + "read": 1, + "write": 1, + "create": 1, + "delete": 0, + "print": 1, + "report": 1, + "export": 1, + "email": 1, + "share": 1 + }, + { + "role": "Production Authority", + "read": 1, + "write": 1, + "create": 0, + "delete": 0, + "print": 1, + "report": 1, + "export": 1, + "email": 1, + "share": 1 + }, + { + "role": "Manufacturing User", + "read": 1, + "write": 0, + "create": 0, + "delete": 0, + "print": 1, + "report": 1, + "export": 0, + "email": 0, + "share": 0 + }, + { + "role": "Stock User", + "read": 1, + "write": 0, + "create": 0, + "delete": 0, + "print": 1, + "report": 1, + "export": 0, + "email": 0, + "share": 0 + }, + { + "role": "Sales User", + "read": 1, + "write": 0, + "create": 0, + "delete": 0, + "print": 1, + "report": 1, + "export": 0, + "email": 0, + "share": 0 + } + ], + "row_format": "Dynamic", + "search_fields": "colour_name,packaging_specification,customer", + "show_title_field_in_link": 0, + "sort_field": "modified", + "sort_order": "DESC", + "states": [], + "title_field": "cylinder_id", + "track_changes": 1 +} diff --git a/makap_backend/makap_backend/doctype/print_cylinder/print_cylinder.py b/makap_backend/makap_backend/doctype/print_cylinder/print_cylinder.py new file mode 100644 index 0000000..5465d0f --- /dev/null +++ b/makap_backend/makap_backend/doctype/print_cylinder/print_cylinder.py @@ -0,0 +1,19 @@ +import frappe +from frappe.model.document import Document + +UNFIT_CONDITIONS = {"Needs Re-chrome", "Damaged"} + + +class PrintCylinder(Document): + def validate(self): + # Work Order release reads "Ready" as fit to print, so a cylinder that + # isn't can't be marked Ready. + if self.status == "Ready" and self.condition in UNFIT_CONDITIONS: + frappe.throw(f"A cylinder in condition '{self.condition}' cannot be Ready. Send it for repair first.") + + if self.impression_limit and (self.impression_count or 0) >= self.impression_limit: + frappe.msgprint( + f"{self.name} has reached its impression limit ({self.impression_limit:,.0f}). Plan a re-chrome or replacement.", + indicator="orange", + alert=True, + )