283 lines
10 KiB
Python
283 lines
10 KiB
Python
# -*- 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, fields, models
|
|
|
|
from ._fp_uom_selection import FP_UOM_SELECTION
|
|
|
|
|
|
class FpStepKind(models.Model):
|
|
"""User-extensible Step Kind catalog.
|
|
|
|
Replaces the hardcoded `default_kind` Selection on fp.step.template
|
|
and fusion.plating.process.node. Each kind carries a list of default
|
|
inputs that get seeded onto a step template when the kind is picked.
|
|
"""
|
|
_name = 'fp.step.kind'
|
|
_description = 'Fusion Plating — Step Kind'
|
|
_order = 'sequence, name'
|
|
|
|
code = fields.Char(
|
|
string='Code', required=True, index=True,
|
|
help='Stable lowercase technical key. Used by automation rules and '
|
|
'workflow-state triggers (e.g. "cleaning", "plate"). Lower-case'
|
|
' enforced; underscores allowed.',
|
|
)
|
|
name = fields.Char(string='Name', required=True, translate=True)
|
|
sequence = fields.Integer(string='Sequence', default=10)
|
|
active = fields.Boolean(string='Active', default=True)
|
|
description = fields.Html(string='Description')
|
|
icon = fields.Selection(
|
|
selection='_get_icon_selection',
|
|
string='Icon',
|
|
default='fa-cog',
|
|
)
|
|
company_id = fields.Many2one(
|
|
'res.company', string='Company',
|
|
default=lambda self: self.env.company,
|
|
)
|
|
default_input_ids = fields.One2many(
|
|
'fp.step.kind.default.input', 'kind_id',
|
|
string='Default Inputs', copy=True,
|
|
help='Auto-seeded onto a step template when this kind is picked '
|
|
'(via the "Seed Defaults" action).',
|
|
)
|
|
template_count = fields.Integer(
|
|
string='Templates', compute='_compute_template_count')
|
|
|
|
_sql_constraints = [
|
|
('fp_step_kind_code_company_uniq',
|
|
'unique(code, company_id)',
|
|
'Step kind code must be unique within a company.'),
|
|
]
|
|
|
|
# Curated FontAwesome 4 icon catalog for the visual icon picker.
|
|
# Bigger than the 24 historical fp.process.node list — covers
|
|
# manufacturing, lab, quality, shipping, safety, time, status etc.
|
|
# FA4 ships with Odoo (no extra deps). Key = CSS class, Value = label.
|
|
_ICON_SELECTION = [
|
|
# Process / chemistry
|
|
('fa-flask', 'Flask / Chemistry'),
|
|
('fa-tint', 'Drop / Liquid'),
|
|
('fa-tachometer', 'Gauge / Measure'),
|
|
('fa-thermometer-half', 'Temp / Heat'),
|
|
('fa-fire', 'Fire / Bake'),
|
|
('fa-snowflake-o', 'Cold / Freeze'),
|
|
('fa-bolt', 'Bolt / Electric'),
|
|
('fa-plug', 'Plug / Power'),
|
|
('fa-magnet', 'Magnet'),
|
|
('fa-bullseye', 'Target / Blast'),
|
|
('fa-shower', 'Shower / Clean'),
|
|
('fa-bathtub', 'Bathtub / Soak'),
|
|
('fa-tachometer', 'Tachometer'),
|
|
|
|
# Equipment / shop
|
|
('fa-industry', 'Industry / Line'),
|
|
('fa-wrench', 'Wrench / Operation'),
|
|
('fa-cog', 'Gear / General'),
|
|
('fa-cogs', 'Gears / System'),
|
|
('fa-sitemap', 'Sitemap / Process'),
|
|
('fa-cubes', 'Cubes / Batch'),
|
|
('fa-cube', 'Cube / Part'),
|
|
('fa-th', 'Grid / Racking'),
|
|
('fa-th-large', 'Large Grid'),
|
|
('fa-server', 'Server / Rack'),
|
|
('fa-database', 'Database / Tank'),
|
|
('fa-archive', 'Archive / Box'),
|
|
('fa-recycle', 'Recycle / Reuse'),
|
|
('fa-balance-scale', 'Scale / Balance'),
|
|
|
|
# Masking / surface
|
|
('fa-paint-brush', 'Paint / Masking'),
|
|
('fa-eraser', 'Eraser / De-Masking'),
|
|
('fa-shield', 'Shield / Protect'),
|
|
('fa-diamond', 'Diamond / Plating'),
|
|
('fa-circle-o-notch', 'Coating Layer'),
|
|
|
|
# Inspection / quality
|
|
('fa-search', 'Search / Inspect'),
|
|
('fa-search-plus', 'Search Plus'),
|
|
('fa-eye', 'Eye / Visual'),
|
|
('fa-eye-slash', 'Eye Slash / Hidden'),
|
|
('fa-camera', 'Camera / Photo'),
|
|
('fa-check', 'Check'),
|
|
('fa-check-circle', 'Check / Approve'),
|
|
('fa-check-square-o', 'Checkbox'),
|
|
('fa-times', 'Reject / Cancel'),
|
|
('fa-times-circle', 'Reject Circle'),
|
|
('fa-exclamation-triangle', 'Warning'),
|
|
('fa-exclamation-circle', 'Alert'),
|
|
('fa-info-circle', 'Info'),
|
|
('fa-question-circle', 'Question'),
|
|
('fa-bug', 'Bug / Defect'),
|
|
('fa-flag', 'Flag'),
|
|
('fa-flag-checkered', 'Finish / Done'),
|
|
('fa-trophy', 'Trophy / Pass'),
|
|
('fa-thumbs-up', 'Thumbs Up'),
|
|
('fa-thumbs-down', 'Thumbs Down'),
|
|
('fa-star', 'Star'),
|
|
('fa-bookmark', 'Bookmark'),
|
|
('fa-certificate', 'Certificate'),
|
|
|
|
# Time
|
|
('fa-clock-o', 'Clock / Wait'),
|
|
('fa-hourglass-half', 'Hourglass'),
|
|
('fa-hourglass-end', 'Hourglass End'),
|
|
('fa-calendar', 'Calendar'),
|
|
('fa-calendar-check-o', 'Scheduled'),
|
|
('fa-history', 'History'),
|
|
|
|
# Safety / handling
|
|
('fa-hand-paper-o', 'Hand / Manual'),
|
|
('fa-hand-stop-o', 'Stop Hand'),
|
|
('fa-life-ring', 'Safety / Life Ring'),
|
|
('fa-medkit', 'First Aid'),
|
|
('fa-user-md', 'Inspector'),
|
|
('fa-lock', 'Lock / Hold'),
|
|
('fa-unlock', 'Unlock / Release'),
|
|
('fa-key', 'Key'),
|
|
|
|
# Documentation / certs
|
|
('fa-file-text-o', 'Document'),
|
|
('fa-file-pdf-o', 'PDF'),
|
|
('fa-file-image-o', 'Image File'),
|
|
('fa-clipboard', 'Clipboard'),
|
|
('fa-list-alt', 'Checklist'),
|
|
('fa-list-ul', 'List'),
|
|
('fa-tags', 'Tags'),
|
|
('fa-tag', 'Tag'),
|
|
('fa-barcode', 'Barcode'),
|
|
('fa-qrcode', 'QR Code'),
|
|
('fa-pencil', 'Pencil'),
|
|
('fa-edit', 'Edit'),
|
|
('fa-print', 'Print'),
|
|
('fa-paperclip', 'Attach'),
|
|
|
|
# Shipping / logistics
|
|
('fa-truck', 'Truck / Receiving'),
|
|
('fa-paper-plane', 'Ship / Send'),
|
|
('fa-plane', 'Plane / Airfreight'),
|
|
('fa-ship', 'Ship'),
|
|
('fa-shopping-cart', 'Cart'),
|
|
('fa-shopping-bag', 'Bag / Pack'),
|
|
('fa-gift', 'Gift / Package'),
|
|
('fa-suitcase', 'Suitcase'),
|
|
('fa-globe', 'Global'),
|
|
('fa-map-marker', 'Location'),
|
|
('fa-road', 'In Transit'),
|
|
|
|
# Status / process flow
|
|
('fa-play-circle', 'Start'),
|
|
('fa-pause-circle', 'Pause / Hold'),
|
|
('fa-stop-circle', 'Stop'),
|
|
('fa-step-forward', 'Step Forward'),
|
|
('fa-fast-forward', 'Fast Forward'),
|
|
('fa-refresh', 'Refresh / Repeat'),
|
|
('fa-undo', 'Undo / Rework'),
|
|
('fa-share', 'Hand-off'),
|
|
('fa-arrow-right', 'Arrow Right'),
|
|
('fa-arrow-down', 'Arrow Down'),
|
|
('fa-long-arrow-right', 'Long Arrow Right'),
|
|
('fa-random', 'Random / Mix'),
|
|
('fa-exchange', 'Exchange'),
|
|
('fa-sort-amount-asc', 'Sort Asc'),
|
|
('fa-sort-amount-desc', 'Sort Desc'),
|
|
('fa-tasks', 'Tasks'),
|
|
|
|
# Misc useful
|
|
('fa-sun-o', 'Sun / Dry'),
|
|
('fa-moon-o', 'Moon / Night'),
|
|
('fa-cloud', 'Cloud'),
|
|
('fa-leaf', 'Leaf / Eco'),
|
|
('fa-tree', 'Tree'),
|
|
('fa-bell', 'Bell / Alert'),
|
|
('fa-bullhorn', 'Announce'),
|
|
('fa-trash', 'Trash / Discard'),
|
|
('fa-plus-circle', 'Add'),
|
|
('fa-minus-circle', 'Remove'),
|
|
('fa-circle', 'Circle'),
|
|
('fa-square', 'Square'),
|
|
('fa-asterisk', 'Asterisk'),
|
|
('fa-cutlery', 'Cutlery / Bend'),
|
|
('fa-link', 'Link / Adhesion'),
|
|
('fa-chain-broken', 'Broken Chain'),
|
|
('fa-anchor', 'Anchor'),
|
|
('fa-ban', 'Ban / Forbidden'),
|
|
]
|
|
|
|
@api.model
|
|
def _get_icon_selection(self):
|
|
return self._ICON_SELECTION
|
|
|
|
@api.depends()
|
|
def _compute_template_count(self):
|
|
Tpl = self.env['fp.step.template']
|
|
for k in self:
|
|
k.template_count = Tpl.search_count([('kind_id', '=', k.id)])
|
|
|
|
@api.model_create_multi
|
|
def create(self, vals_list):
|
|
for v in vals_list:
|
|
if v.get('code'):
|
|
v['code'] = v['code'].lower().strip().replace(' ', '_')
|
|
return super().create(vals_list)
|
|
|
|
def write(self, vals):
|
|
if vals.get('code'):
|
|
vals['code'] = vals['code'].lower().strip().replace(' ', '_')
|
|
return super().write(vals)
|
|
|
|
def action_open_templates(self):
|
|
self.ensure_one()
|
|
return {
|
|
'type': 'ir.actions.act_window',
|
|
'name': _('Step Templates — %s') % self.name,
|
|
'res_model': 'fp.step.template',
|
|
'view_mode': 'list,form',
|
|
'domain': [('kind_id', '=', self.id)],
|
|
'context': {'default_kind_id': self.id},
|
|
}
|
|
|
|
|
|
class FpStepKindDefaultInput(models.Model):
|
|
"""Default input prototype attached to a step kind.
|
|
|
|
When a recipe author picks a kind on a step template and clicks
|
|
'Seed Defaults', these get copied into the template's input list
|
|
(idempotent — skips by name).
|
|
"""
|
|
_name = 'fp.step.kind.default.input'
|
|
_description = 'Fusion Plating — Step Kind Default Input'
|
|
_order = 'sequence, name'
|
|
|
|
name = fields.Char(string='Name', required=True, translate=True)
|
|
kind_id = fields.Many2one(
|
|
'fp.step.kind', string='Kind',
|
|
required=True, ondelete='cascade', index=True,
|
|
)
|
|
input_type = fields.Selection([
|
|
('text', 'Text'),
|
|
('number', 'Number'),
|
|
('boolean', 'Yes/No'),
|
|
('selection', 'Selection'),
|
|
('date', 'Date / Time'),
|
|
('signature', 'Signature'),
|
|
('time_hms', 'Time (HH:MM:SS)'),
|
|
('time_seconds', 'Time (seconds)'),
|
|
('temperature', 'Temperature'),
|
|
('thickness', 'Thickness'),
|
|
('pass_fail', 'Pass / Fail'),
|
|
('photo', 'Photo'),
|
|
('multi_point_thickness', 'Multi-Point Thickness (avg)'),
|
|
('bath_chemistry_panel', 'Bath Chemistry Panel'),
|
|
('ph', 'pH'),
|
|
], string='Input Type', required=True, default='text')
|
|
target_unit = fields.Selection(FP_UOM_SELECTION, string='Target Unit')
|
|
required = fields.Boolean(string='Required', default=False)
|
|
hint = fields.Char(string='Hint')
|
|
selection_options = fields.Text(string='Selection Options',
|
|
help='Comma-separated when input_type is "selection".')
|
|
sequence = fields.Integer(string='Sequence', default=10)
|