Stock Transform Documentation¶
Overview¶
The Stock Transform module (stock.transform) manages the conversion or transformation of materials and products. It handles manufacturing processes, assembly operations, disassembly, and any workflow where input materials are converted into output products. This is the core module for production and manufacturing in the system.
Model Information¶
Model Name: stock.transform
Display Name: Product Transform
Name Field: number
Key Fields: N/A
Features¶
- ✅ Audit logging enabled (
_audit_log = True) - ✅ Automated sequence numbering by layer type
- ✅ State workflow management
- ✅ Transform location management
- ✅ Cost calculation and tracking
- ✅ Template-based transforms
- ✅ Lot assignment automation
State Workflow¶
| State | Description |
|---|---|
draft |
Initial state, can be edited |
confirmed |
Stock moves created but not validated |
done |
Completed, stock moves validated |
voided |
Cancelled/voided |
Layer Types¶
| Type | Code | Description | Sequence |
|---|---|---|---|
| Back Layer | BL |
Back layer production | layer_back |
| Middle Layer | ML |
Middle layer production | layer_middle |
| Upper Layer | UL |
Upper layer production | layer_upper |
| Laminating | LAM |
Laminating process | layer_laminating |
| Printing | PRT |
Printing process | layer_printing |
Each layer type has its own sequence numbering.
Key Fields Reference¶
Header Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
number |
Char | ✅ | Auto-generated transform number (by layer type) |
date |
Date | ✅ | Transform date |
journal_id |
Many2One | ❌ | Stock journal (stock.journal) |
final_product_code |
Many2One | ❌ | Final product code (product) |
state |
Selection | ✅ | Current workflow state |
notes |
Text | ❌ | Additional notes |
ref |
Char | ❌ | External reference |
project_id |
Many2One | ❌ | Associated project |
date_done |
DateTime | ❌ | Completion timestamp |
date_start |
DateTime | ❌ | Start timestamp |
check_ratio |
Char | ❌ | Quality check ratio |
job_order |
Many2One | ❌ | Job order reference (job.order) |
pt_template |
Many2One | ❌ | Transform template (product.transform.template) |
layer_type |
Selection | ❌ | Layer type for sequencing |
Roller Fields (for printing/laminating)¶
| Field | Type | Description |
|---|---|---|
roller_no_1 |
Many2One | Roller number 1 (color.code) |
roller_no_2 |
Many2One | Roller number 2 (color.code) |
roller_no_3 |
Many2One | Roller number 3 (color.code) |
roller_no_4 |
Many2One | Roller number 4 (color.code) |
Computed Fields¶
| Field | Description |
|---|---|
qty_in |
Total input quantity |
qty_out |
Total output quantity |
qty_diff |
Difference (output - input) |
cost_total_in |
Total cost of input products |
cost_total_out |
Total cost of output products |
area_total_in |
Total area (M²) of inputs |
area_total_out |
Total area (M²) of outputs |
Relationship Fields¶
| Field | Type | Description |
|---|---|---|
lines |
One2Many | All transform lines (stock.transform.line) |
lines_in |
One2Many | Input product lines (type="in" or "balance") |
lines_out |
One2Many | Output product lines (type="out") |
lines_balance |
One2Many | Balance/remainder lines (type="balance") |
lines_service |
One2Many | Service lines (type="service") |
stock_moves |
One2Many | Stock movements (stock.move) |
purchase_orders |
One2Many | Related purchase orders |
related_id |
Reference | Related document (purchase.order, stock.picking) |
picking_id |
Many2One | Associated picking |
sale_id |
Many2One | Associated sales order |
API Methods¶
1. Create Transform¶
Method: create(vals, context)
Creates a new stock transform with automatic stock move creation.
Parameters:
vals = {
"date": "2025-10-27", # Required: Transform date
"layer_type": "BL", # Optional: For sequencing
"final_product_code": 123, # Optional: Final product
"pt_template": 456, # Optional: Template to use
"lines_in": [ # Input products
("create", {
"type": "in",
"product_id": 789,
"qty": 100,
"uom_id": 1,
"location_id": 201,
"lot_id": 301
})
],
"lines_out": [ # Output products
("create", {
"type": "out",
"product_id": 999,
"qty": 95,
"uom_id": 1,
"location_id": 201
})
]
}
context = {
"layer_type": "BL", # For sequence generation
"date": "2025-10-27"
}
Returns: int - New record ID
Behavior: - Generates sequence number based on layer_type - Checks for duplicate numbers - Creates stock moves immediately (in draft) - Links to job order if specified
Example:
# Create transform from raw material to finished product
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"layer_type": "BL",
"lines_in": [
("create", {
"type": "in",
"product_id": raw_mat_id,
"qty": 1000,
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id
})
],
"lines_out": [
("create", {
"type": "out",
"product_id": finished_prod_id,
"qty": 950,
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id
})
]
}, context={"layer_type": "BL"})
2. Confirm Transform¶
Method: confirm(ids, context)
Creates stock movements through transform location.
Parameters:
- ids (list): Transform IDs
Behavior: - Finds transform location (type="transform") - Creates "in" moves from source to transform location - Creates "out" moves from transform to destination - Sets moves to "approved" state (not done yet) - Sets transform state to "confirmed"
Example:
# Confirm transform to create stock moves
get_model("stock.transform").confirm([transform_id])
# Verify moves created
transform = get_model("stock.transform").browse(transform_id)
assert transform.state == "confirmed"
assert len(transform.stock_moves) > 0
3. Validate Transform¶
Method: validate(ids, context)
Confirms AND validates transform in one step.
Parameters:
- ids (list): Transform IDs
Behavior: - Runs confirm logic if not already confirmed - Validates all stock moves (sets to "done") - Updates costs - Checks cost balance - Sets transform state to "done"
Example:
# Validate transform (creates and completes stock moves)
get_model("stock.transform").validate([transform_id])
# Verify completion
transform = get_model("stock.transform").browse(transform_id)
assert transform.state == "done"
4. Validate Manually¶
Method: validateManually(ids, context)
Validates existing stock moves without creating new ones.
Parameters:
- ids (list): Transform IDs
Behavior: - Validates existing stock moves only - Updates costs - Checks cost balance
Example:
# Validate manually if moves already exist
get_model("stock.transform").validateManually([transform_id])
5. Void Transform¶
Method: void(ids, context)
Cancels transform and deletes stock movements.
Parameters:
- ids (list): Transform IDs
Example:
6. Revert to Draft¶
Method: to_draft(ids, context)
Reverts transform to draft, deleting stock movements.
Parameters:
- ids (list): Transform IDs
Example:
7. Update Costs¶
Method: update_cost(ids, context)
Recalculates and updates product costs based on transform.
Behavior: - Computes cost for all input products - Distributes total input cost across output products - Updates move costs and product costs - Handles multiple outputs and cost groups
Example:
8. Assign Lots¶
Method: assign_lots(ids, match_qty2=False, context)
Automatically assigns available lots to input lines.
Parameters:
- ids (list): Transform IDs
- match_qty2 (bool): Match by qty2 (weight) instead of qty
Behavior: - Finds available lots for each input product - Assigns lots based on product's lot selection strategy (FIFO/FEFO/qty) - Creates multiple lines if multiple lots needed - Respects min_life_remain_percent and max_lots_per_sale settings
Example:
# Auto-assign lots to input materials
get_model("stock.transform").assign_lots([transform_id])
# Or match by weight
get_model("stock.transform").assign_lots([transform_id], match_qty2=True)
9. Copy to Purchase Order¶
Method: copy_to_purchase(ids, context)
Creates purchase orders for service lines.
Parameters:
- ids (list): Transform ID
Behavior: - Groups service lines by supplier - Creates purchase order for each supplier - Links PO to transform via related_id
Returns: Dictionary with flash message
Example:
# Create POs for outsourced services
result = get_model("stock.transform").copy_to_purchase([transform_id])
print(result["flash"]) # "Purchase order created successfully: PO-001, PO-002"
10. Update Lot Balance¶
Method: update_lot_balance(ids, context)
Creates balance lines for remaining lot quantities.
Parameters:
- ids (list): Transform IDs
Behavior:
- Checks products with pt_balance_remove=True
- Calculates remaining quantity in lot after transform
- Creates balance lines for remainder
Example:
UI Events (onchange methods)¶
onchange_layertype¶
Triggered when layer type changes. Updates: - Number field with new sequence based on layer type
onchange_product¶
Triggered when final_product_code changes. Updates: - pt_template if matching template found - Loads template lines into lines_in and lines_out
onchange_PT_template¶
Triggered when template selected. Updates: - lines_in from template - lines_out from template - Copies planned quantities, dimensions, locations
Computed Fields Functions¶
get_total_qty(ids, context)¶
Calculates: - qty_in: Sum of all input line quantities - qty_out: Sum of all output line quantities - qty_diff: qty_out - qty_in
get_total_cost(ids, context)¶
Calculates: - cost_total_in: Sum of input line costs - cost_total_out: Sum of output line costs - area_total_in: Sum of input areas - area_total_out: Sum of output areas
Configuration Settings¶
Required Settings¶
| Setting | Location | Description |
|---|---|---|
transform_journal_id |
settings (ID:1) | Journal for transform transactions |
Required System Data¶
- Transform location must exist (type="transform")
- Sequence for each layer type (layer_back, layer_middle, etc.)
Related Models¶
| Model | Relationship | Description |
|---|---|---|
stock.transform.line |
One2Many | Transform line items |
product |
Many2One | Input/output products |
stock.lot |
Many2One | Lot tracking |
stock.location |
Many2One | Storage locations |
stock.move |
One2Many | Stock movements |
stock.journal |
Many2One | Transaction journal |
uom |
Many2One | Units of measure |
purchase.order |
One2Many | Service purchase orders |
project |
Many2One | Project allocation |
job.order |
Many2One | Job order reference |
product.transform.template |
Many2One | Transform template/recipe |
Common Use Cases¶
Use Case 1: Simple Material Transform¶
# Convert raw material to finished product
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"lines_in": [
("create", {
"type": "in",
"product_id": raw_material_id,
"qty": 100,
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id
})
],
"lines_out": [
("create", {
"type": "out",
"product_id": finished_product_id,
"qty": 95, # 5% waste
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id
})
]
})
# Validate to complete transform
get_model("stock.transform").validate([transform_id])
Use Case 2: Multi-Input Assembly¶
# Assemble product from multiple components
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"final_product_code": laptop_id,
"lines_in": [
("create", {
"type": "in",
"product_id": motherboard_id,
"qty": 1,
"uom_id": unit_uom_id,
"location_id": warehouse_loc_id
}),
("create", {
"type": "in",
"product_id": screen_id,
"qty": 1,
"uom_id": unit_uom_id,
"location_id": warehouse_loc_id
}),
("create", {
"type": "in",
"product_id": battery_id,
"qty": 1,
"uom_id": unit_uom_id,
"location_id": warehouse_loc_id
})
],
"lines_out": [
("create", {
"type": "out",
"product_id": laptop_id,
"qty": 1,
"uom_id": unit_uom_id,
"location_id": warehouse_loc_id
})
]
})
get_model("stock.transform").validate([transform_id])
Use Case 3: Transform with Template¶
# Use template for standard transform
# 1. Find template
template = get_model("product.transform.template").search_browse([
["fg_product", "=", widget_id]
])[0]
# 2. Create transform from template
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"final_product_code": widget_id,
"pt_template": template.id
# Lines automatically loaded from template via onchange
})
# 3. Validate
get_model("stock.transform").validate([transform_id])
Best Practices¶
1. Use Templates for Repeated Transforms¶
# Good: Create template for standard process
template_id = get_model("product.transform.template").create({
"name": "Widget Assembly Standard",
"fg_product": widget_id,
"lines_in": [...],
"lines_out": [...]
})
# Then create transforms from template
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"pt_template": template_id
}) # ✅ Consistent, fast
2. Always Balance Input/Output Costs¶
# Good: Let system calculate output costs
transform_id = get_model("stock.transform").create({
"lines_in": [
("create", {"product_id": p1, "qty": 10, "cost_price": 5}) # Cost: 50
],
"lines_out": [
("create", {"product_id": p2, "qty": 8}) # Cost auto-calculated: 6.25
]
})
get_model("stock.transform").validate([transform_id])
get_model("stock.transform").update_cost([transform_id]) # ✅ Balanced
3. Use Layer Types for Organized Numbering¶
# Good: Specify layer type for clear sequencing
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"layer_type": "BL", # ✅ Gets BL-2025-001
# ...
}, context={"layer_type": "BL"})
Troubleshooting¶
"Missing transform location"¶
Cause: No location with type="transform" exists
Solution: Create transform location in system
"Missing transform journal"¶
Cause: transform_journal_id not configured in settings
Solution: Configure transform journal in inventory settings
"Can not delete product transforms in this status"¶
Cause: Trying to delete completed transform
Solution: Only delete draft transforms; void or revert to draft first
"{number} number already exist in system"¶
Cause: Duplicate transform number
Solution: System should auto-increment; check sequence configuration
"From products cost different than to products cost"¶
Cause: Input and output costs don't match
Solution: Run update_cost to balance, or adjust product costs
Version History¶
Last Updated: 2025-10-27
Model Version: stock_transform.py
Framework: Netforce
Additional Resources¶
- Stock Transform Line Documentation:
stock.transform.line - Product Transform Template Documentation:
product.transform.template - Stock Move Documentation:
stock.move - Product Documentation:
product
This documentation is generated for developer onboarding and reference purposes.