Skip to content

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:

get_model("stock.transform.line").delete([line_id])


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:

line = get_model("stock.transform.line").browse(line_id)
print(f"Weight: {line.weight} kg")


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!")


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.