Stock Transform Line Documentation¶
Overview¶
The Stock Transform Line module (stock.transform.line) represents individual input, output, service, or balance items within a stock transform. Each line tracks product details, quantities, costs, locations, and supports advanced features like cost groups, lot tracking, and dimensional specifications.
Model Information¶
Model Name: stock.transform.line
Display Name: Transform Line
Key Fields: N/A
Features¶
- ✅ Multiple line types (in/out/service/balance)
- ✅ Cost tracking and calculation
- ✅ Lot and location tracking
- ✅ Dimensional specifications (width, thickness, area)
- ✅ Stock availability checking
- ❌ Audit logging
Line Types¶
| Type | Code | Description |
|---|---|---|
| From | in |
Input materials/products consumed |
| To | out |
Output/finished products created |
| Service | service |
Services (e.g., subcontracting) |
| Lot Balance | balance |
Remaining balance from lot |
Key Fields Reference¶
Basic Fields¶
| Field | Type | Required | Description |
|---|---|---|---|
transform_id |
Many2One | ✅ | Parent transform (stock.transform) |
type |
Selection | ✅ | Line type (in/out/service/balance) |
product_id |
Many2One | ✅ | Product (product, stock type) |
qty |
Decimal | ✅ | Quantity (scale: 6) |
uom_id |
Many2One | ✅ | Unit of measure (uom) |
location_id |
Many2One | ❌ | Stock location (stock.location, internal) |
Cost Fields¶
| Field | Type | Description |
|---|---|---|
cost_price |
Decimal | Cost per unit (computed from move) |
cost_amount |
Decimal | Total cost (qty * cost_price) |
cost_group |
Selection | Cost grouping (balance/fixed_price/1-9) |
amount |
Decimal | Line amount |
Quantity & Measurement Fields¶
| Field | Type | Description |
|---|---|---|
uom2_id |
Many2One | Secondary UoM |
qty2 |
Decimal | Secondary quantity |
planned_qty |
Decimal | Planned quantity |
thickness |
Decimal | Thickness in mm (scale: 2) |
width |
Decimal | Width in mm |
area_m2 |
Decimal | Area in square meters |
gross_wt |
Decimal | Gross weight |
net_wt |
Decimal | Net weight in kg |
weight |
Decimal | Gross weight (computed) |
Tracking Fields¶
| Field | Type | Description |
|---|---|---|
lot_id |
Many2One | Lot/Serial number (stock.lot) |
move_id |
Many2One | Associated stock move (stock.move) |
picking_id |
Many2One | Associated picking (stock.picking) |
supplier_id |
Many2One | Supplier (for service lines) (contact) |
confirmed |
Boolean | Confirmation flag |
date |
DateTime | Line date/time |
timestamp |
DateTime | Timestamp |
Material Specification Fields¶
| Field | Type | Description |
|---|---|---|
master_prod_code |
Char | Master product code |
prod_code |
Char | Product code |
material_categ |
Many2One | Material category (material.categ) |
defect_categ |
Many2One | Defect category (defect.categ) |
color_code |
Many2One | Color code (color.code) |
pattern_code |
Many2One | Pattern code (pattern.code) |
sa_id |
Many2One | SA number reference (sa.number) |
Printing/Process Fields¶
| Field | Type | Description |
|---|---|---|
roller_no |
Text | Roller numbers |
layer_no |
Selection | Layer number (1/2/3) |
ink_type |
Selection | Ink type (old_ink/new_ink/mix_ink) |
preset_duration |
Decimal | Preset duration |
Other Fields¶
| Field | Type | Description |
|---|---|---|
notes |
Text | Line notes |
qty_stock |
Decimal | Available stock (computed) |
API Methods¶
1. Create Transform Line¶
Method: create(vals, context)
Creates a new transform line.
Parameters:
vals = {
"transform_id": 123, # Required: Parent transform
"type": "in", # Required: Line type
"product_id": 456, # Required: Product ID
"qty": 100, # Required: Quantity
"uom_id": 1, # Required: UoM
"location_id": 789, # Optional: Location
"lot_id": 101, # Optional: Lot/serial
"cost_price": 10.50, # Optional: Cost
"width": 1200, # Optional: Width (mm)
"thickness": 2.5, # Optional: Thickness (mm)
"area_m2": 5.0 # Optional: Area
}
Returns: int - New line ID
Example:
# Add input line to transform
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "in",
"product_id": raw_material_id,
"qty": 100,
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id,
"lot_id": lot_id
})
2. Update Line¶
Method: write(ids, vals, context)
Updates transform line data.
Example:
line = get_model("stock.transform.line").browse(line_id)
# Update quantity
line.write({"qty": 150})
# Update cost
line.write({"cost_price": 12.00})
# Update multiple fields
line.write({
"qty": 150,
"cost_price": 12.00,
"notes": "Revised quantity"
})
3. Delete Line¶
Method: delete(ids, context)
Deletes a transform line.
Example:
Computed Fields Functions¶
get_cost_price(ids, context)¶
Returns cost price from associated stock move.
Example:
line = get_model("stock.transform.line").browse(line_id)
print(f"Cost: {line.cost_price}") # From move_id.cost_price
get_amount_price(ids, context) / get_amount(ids, context)¶
Calculates total cost amount (cost_price * qty).
Example:
line = get_model("stock.transform.line").browse(line_id)
print(f"Total: {line.cost_amount}") # qty * cost_price
get_gross_weight(ids, context)¶
Returns weight from lot or product.
Behavior: - First checks lot_id.weight - Falls back to product_id.weight
Example:
get_qty_stock(ids, context)¶
Returns available stock for product/lot/location at transform date.
Example:
line = get_model("stock.transform.line").browse(line_id)
print(f"Available: {line.qty_stock}")
print(f"Requested: {line.qty}")
if line.qty_stock < line.qty:
print("⚠️ Insufficient stock!")
Related Models¶
| Model | Relationship | Description |
|---|---|---|
stock.transform |
Many2One | Parent transform |
product |
Many2One | Product referenced |
stock.lot |
Many2One | Lot/serial tracking |
stock.location |
Many2One | Storage location |
uom |
Many2One | Unit of measure |
stock.move |
Many2One | Associated stock movement |
stock.picking |
Many2One | Associated picking |
contact |
Many2One | Supplier (for services) |
Common Use Cases¶
Use Case 1: Add Input Line¶
# Add raw material input to transform
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "in",
"product_id": raw_material_id,
"qty": 100,
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id,
"lot_id": lot_id
})
Use Case 2: Add Output Line¶
# Add finished product output to transform
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "out",
"product_id": finished_product_id,
"qty": 95, # 5% waste
"uom_id": kg_uom_id,
"location_id": warehouse_loc_id
})
Use Case 3: Add Service Line¶
# Add outsourced service to transform
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "service",
"product_id": service_product_id,
"qty": 100,
"uom_id": unit_uom_id,
"supplier_id": supplier_id,
"cost_price": 5.0 # Cost per unit
})
Use Case 4: Add Line with Dimensions¶
# Add line with dimensional specifications
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "in",
"product_id": sheet_material_id,
"qty": 10,
"uom_id": sheet_uom_id,
"location_id": warehouse_loc_id,
"width": 1200, # 1200mm
"thickness": 2.5, # 2.5mm
"area_m2": 12.0 # 12 square meters
})
Use Case 5: Check Stock Availability¶
# Verify sufficient stock before transform
line = get_model("stock.transform.line").browse(line_id)
if line.type == "in":
available = line.qty_stock
requested = line.qty
if available < requested:
print(f"⚠️ Insufficient stock!")
print(f" Available: {available}")
print(f" Requested: {requested}")
print(f" Short: {requested - available}")
else:
print(f"✅ Sufficient stock available")
Best Practices¶
1. Always Specify Location for Stock Items¶
# Good: Specify location
line_vals = {
"type": "in",
"product_id": product_id,
"qty": 100,
"location_id": warehouse_loc_id # ✅ Clear source
}
# Bad: No location
line_vals = {
"type": "in",
"product_id": product_id,
"qty": 100
# ❌ System may not know where to get stock
}
2. Use Lot Tracking for Traceability¶
# Good: Track by lot
line_vals = {
"type": "in",
"product_id": product_id,
"qty": 100,
"lot_id": lot_id # ✅ Full traceability
}
# Bad: No lot for tracked product
line_vals = {
"type": "in",
"product_id": product_id, # Product requires lot tracking
"qty": 100
# ❌ Cannot trace source
}
3. Check Stock Before Creating Lines¶
# Good: Verify stock availability
available = get_available_stock(product_id, location_id, lot_id)
if available >= requested_qty:
line_id = get_model("stock.transform.line").create({
"type": "in",
"product_id": product_id,
"qty": requested_qty,
"location_id": location_id,
"lot_id": lot_id
}) # ✅ Safe to create
else:
print(f"Insufficient stock: {available} < {requested_qty}")
# Bad: Create without checking
line_id = get_model("stock.transform.line").create({...}) # ❌ May fail on validation
4. Use Cost Groups for Complex Costing¶
# For multi-output transforms with different costing logic
# Fixed price output
line_vals = {
"type": "out",
"product_id": premium_product_id,
"qty": 10,
"cost_group": "fixed_price" # Uses product.fixed_price
}
# Cost group 1
line_vals = {
"type": "out",
"product_id": standard_product_id,
"qty": 80,
"cost_group": "1" # Proportional allocation within group
}
# Balance (gets remaining cost)
line_vals = {
"type": "out",
"product_id": byproduct_id,
"qty": 5,
"cost_group": "balance" # Gets leftover cost
}
Search Functions¶
Search by Transform¶
# Find all lines for a transform
condition = [["transform_id", "=", transform_id]]
lines = get_model("stock.transform.line").search(condition)
Search by Type¶
# Find all input lines
condition = [["type", "=", "in"]]
input_lines = get_model("stock.transform.line").search(condition)
# Find all output lines
condition = [["type", "=", "out"]]
output_lines = get_model("stock.transform.line").search(condition)
Search by Product¶
# Find transforms using specific product
condition = [["product_id", "=", product_id]]
lines = get_model("stock.transform.line").search(condition)
Search by Location¶
# Find lines from specific location
condition = [["location_id", "=", location_id]]
lines = get_model("stock.transform.line").search(condition)
Search by Date Range¶
# Find lines created in date range
condition = [
["date", ">=", "2025-10-01"],
["date", "<=", "2025-10-31"]
]
lines = get_model("stock.transform.line").search(condition)
Performance Tips¶
1. Access Lines Through Parent¶
# Efficient: Load transform with lines
transform = get_model("stock.transform").browse(transform_id)
for line in transform.lines_in: # ✅ Single query
print(line.product_id.name)
# Less efficient: Query each line
line_ids = get_model("stock.transform.line").search([
["transform_id", "=", transform_id],
["type", "=", "in"]
])
for line_id in line_ids:
line = get_model("stock.transform.line").browse(line_id) # ❌ Multiple queries
2. Batch Operations¶
# Efficient: Batch create lines
lines_data = [
{"type": "in", "product_id": p1, "qty": 10, ...},
{"type": "in", "product_id": p2, "qty": 5, ...},
{"type": "out", "product_id": p3, "qty": 14, ...}
]
transform_id = get_model("stock.transform").create({
"date": "2025-10-27",
"lines": [("create", line_data) for line_data in lines_data]
}) # ✅ Single transaction
3. Computed Fields Cache¶
# Computed fields recalculate on each access
line = get_model("stock.transform.line").browse(line_id)
# Cache if using multiple times
cost_price = line.cost_price
cost_amount = line.cost_amount
qty_stock = line.qty_stock
# Use cached values
if qty_stock < line.qty:
print(f"Short: {line.qty - qty_stock}, Cost impact: ${cost_amount}")
Troubleshooting¶
Cost price is None¶
Cause: No stock move created yet or move has no cost
Solution: Validate transform to create moves, or set cost_price directly
qty_stock shows 0 but stock exists¶
Cause: Wrong location or lot specified
Solution: Verify location_id and lot_id match actual stock
Cannot create line - product type error¶
Cause: Product is not stock type
Solution: Only stock-type products can be used in transform lines
Line not showing in transform¶
Cause: Wrong type filter (lines_in vs lines_out)
Solution: Check line.type matches the relationship being accessed
Testing Examples¶
Unit Test: Create Input Line¶
def test_create_input_line():
transform_id = get_model("stock.transform").create({
"date": "2025-10-27"
})
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "in",
"product_id": prod_id,
"qty": 100,
"uom_id": uom_id,
"location_id": loc_id
})
line = get_model("stock.transform.line").browse(line_id)
assert line.transform_id.id == transform_id
assert line.type == "in"
assert line.qty == 100
Unit Test: Cost Calculation¶
def test_cost_calculation():
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "in",
"product_id": prod_id,
"qty": 10,
"uom_id": uom_id,
"location_id": loc_id,
"cost_price": 5.00
})
line = get_model("stock.transform.line").browse(line_id)
assert line.cost_amount == 50.00 # 10 * 5.00
Unit Test: Stock Availability Check¶
def test_stock_availability():
# Setup: Create stock
create_stock(prod_id, lot_id, loc_id, qty=50)
# Create line requesting 100
line_id = get_model("stock.transform.line").create({
"transform_id": transform_id,
"type": "in",
"product_id": prod_id,
"qty": 100,
"uom_id": uom_id,
"location_id": loc_id,
"lot_id": lot_id
})
line = get_model("stock.transform.line").browse(line_id)
assert line.qty_stock == 50
assert line.qty_stock < line.qty # Insufficient
Version History¶
Last Updated: 2025-10-27
Model Version: stock_transform_line.py
Framework: Netforce
Additional Resources¶
- Stock Transform Documentation:
stock.transform - Product Documentation:
product - Stock Location Documentation:
stock.location - Stock Move Documentation:
stock.move
This documentation is generated for developer onboarding and reference purposes.