Skip to content

QC Result Documentation

Overview

The QC Result module (qc.result) records quality control inspection results for products during stock movements or production. It captures test results, inspector information, and accept/reject decisions with supporting documentation.


Model Information

Model Name: qc.result
Display Name: QC Result
Key Fields: None (no unique constraint defined)

Features

  • ❌ Audit logging enabled (_audit_log)
  • ❌ Multi-company support (company_id)
  • ❌ Full-text content search (_content_search)
  • ❌ Unique key constraint
  • ✅ Custom default ordering by date and ID

Sort Order: date,id (most recent first)


Field Reference

Header Fields

Field Type Required Description
date Date Date of quality control inspection (searchable)
result Selection Final QC decision: "accept" or "reject" (searchable)
total_qty Decimal Total quantity being inspected
sample_qty Decimal Sample quantity tested from total
image File Photo documentation of inspection or defects

Product and Lot Tracking

Field Type Description
product_id Many2One Product being inspected (searchable)
lot_id Many2One Lot or serial number of the product (searchable)
uom_id Many2One Unit of measure for quantities

Test Results

Field Type Description
test1 Selection Test 1 result: "1" (pass) or "0" (fail)
test2 Selection Test 2 result: "1" (pass) or "0" (fail)
test3 Selection Test 3 result: "1" (pass) or "0" (fail)
test4 Selection Test 4 result: "1" (pass) or "0" (fail)
test5 Selection Test 5 result: "1" (pass) or "0" (fail)
test6 Selection Test 6 result: "1" (pass) or "0" (fail)
test7 Selection Test 7 result: "1" (pass) or "0" (fail)
test8 Selection Test 8 result: "1" (pass) or "0" (fail)

Personnel

Field Type Description
inspector_id Many2One User who performed the inspection (searchable)
reviewer_id Many2One User who reviewed the inspection results (searchable)
Field Type Description
pick_id Many2One Related stock picking (on_delete="cascade", searchable)
production_id Many2One Related production order (on_delete="cascade", searchable)
move_id Many2One Specific stock move being inspected

QC Result Types

Type Code Description
Accept accept Product passed quality inspection and is approved
Reject reject Product failed quality inspection and is rejected

Test Result Values

Each test (test1-test8) can have two values:

Value Code Meaning
Pass 1 Test passed
Fail 0 Test failed

The tests are generic and can be configured to represent specific quality checks relevant to the business (e.g., dimensional accuracy, visual inspection, functional test, etc.).


API Methods

1. Create QC Result

Method: create(vals, context)

Creates a new quality control result record.

Parameters:

vals = {
    "date": "2025-10-27",           # Inspection date
    "product_id": 123,               # Product being inspected
    "lot_id": 456,                   # Lot/serial number
    "total_qty": 100.0,              # Total quantity
    "sample_qty": 10.0,              # Sample size
    "uom_id": 1,                     # Unit of measure
    "test1": "1",                    # Test results
    "test2": "1",
    "test3": "0",                    # Failed test
    "result": "reject",              # Overall result
    "inspector_id": 789,             # Inspector
    "pick_id": 101,                  # Related picking (optional)
    "move_id": 202,                  # Related stock move (optional)
}

Returns: int - New QC result ID

Example:

# Record a QC inspection result
qc_result_id = get_model("qc.result").create({
    "date": "2025-10-27",
    "product_id": product_id,
    "lot_id": lot_id,
    "total_qty": 100.0,
    "sample_qty": 10.0,
    "uom_id": uom_id,
    "test1": "1",  # Pass
    "test2": "1",  # Pass
    "test3": "1",  # Pass
    "test4": "1",  # Pass
    "result": "accept",
    "inspector_id": user_id,
    "pick_id": picking_id
})


2. Default Get (Pre-fill from Stock Move)

Method: default_get(field_names, context, **kw)

Automatically populates QC result fields from a stock move when creating from a move context.

Context:

context = {
    "move_id": 123  # Stock move ID to pull defaults from
}

Behavior: - Retrieves stock move details - Pre-fills product, lot, quantity, UoM, and picking information - Simplifies QC result creation from stock movement screens

Example:

# Create QC result from stock move context
qc_result_id = get_model("qc.result").create(
    {},
    context={"move_id": move_id}
)
# Product, lot, qty, etc. are automatically filled from the move

Pre-filled Fields: - pick_id - From move.picking_id - move_id - The stock move itself - product_id - From move.product_id - lot_id - From move.lot_id (if exists) - total_qty - From move.qty - uom_id - From move.uom_id


3. Dummy Method

Method: dummy(ids, context)

Placeholder method with no functionality. May be used for UI interactions or future implementation.


Model Relationship Description
stock.picking Many2One Stock picking operation being inspected
production.order Many2One Production order being inspected
stock.move Many2One Specific stock movement being inspected
product Many2One Product being inspected
stock.lot Many2One Lot/serial number of product
uom Many2One Unit of measure for quantities
base.user Many2One Inspector and reviewer users

Common Use Cases

Use Case 1: Record Incoming Material Inspection

# QC inspection on received materials
def record_receiving_inspection(picking_id, move_id):
    move = get_model("stock.move").browse(move_id)

    qc_result_id = get_model("qc.result").create({
        "date": time.strftime("%Y-%m-%d"),
        "pick_id": picking_id,
        "move_id": move_id,
        "product_id": move.product_id.id,
        "lot_id": move.lot_id.id if move.lot_id else None,
        "total_qty": move.qty,
        "sample_qty": min(10, move.qty),  # Sample size
        "uom_id": move.uom_id.id,
        "test1": "1",  # Packaging condition
        "test2": "1",  # Visual inspection
        "test3": "1",  # Documentation check
        "result": "accept",
        "inspector_id": get_active_user(),
    })

    return qc_result_id

Use Case 2: Production Output Inspection

# QC inspection on finished goods
def record_production_inspection(production_id, product_id, qty):
    qc_result_id = get_model("qc.result").create({
        "date": time.strftime("%Y-%m-%d"),
        "production_id": production_id,
        "product_id": product_id,
        "total_qty": qty,
        "sample_qty": 5,
        "test1": "1",  # Dimensional check
        "test2": "1",  # Functional test
        "test3": "0",  # Visual defects detected
        "test4": "1",  # Performance test
        "result": "reject",  # Failed due to test3
        "inspector_id": get_active_user(),
    }, context={"production_id": production_id})

    return qc_result_id

Use Case 3: Create QC Result from Stock Move UI

# When user clicks "Create QC Result" on a stock move
# The system automatically pre-fills fields

# In the UI action:
action = {
    "name": "QC Result",
    "view": "qc_result_form",
    "model": "qc.result",
    "context": {
        "move_id": current_move_id  # Triggers default_get
    }
}

# default_get automatically populates:
# - Product
# - Lot
# - Quantity
# - UoM
# - Picking reference

Use Case 4: Batch Inspection with Photo Documentation

# Record inspection with photo evidence
def record_inspection_with_photo(move_id, test_results, photo_path):
    move = get_model("stock.move").browse(move_id)

    # Determine overall result based on tests
    all_passed = all(result == "1" for result in test_results.values())

    qc_result_id = get_model("qc.result").create({
        "date": time.strftime("%Y-%m-%d"),
        "move_id": move_id,
        "pick_id": move.picking_id.id,
        "product_id": move.product_id.id,
        "lot_id": move.lot_id.id if move.lot_id else None,
        "total_qty": move.qty,
        "sample_qty": test_results.get("sample_qty", 1),
        "test1": test_results.get("test1"),
        "test2": test_results.get("test2"),
        "test3": test_results.get("test3"),
        "test4": test_results.get("test4"),
        "result": "accept" if all_passed else "reject",
        "inspector_id": get_active_user(),
        "image": photo_path,  # Photo evidence
    })

    return qc_result_id

Use Case 5: QC Result Analysis

# Analyze QC performance metrics
def analyze_qc_results(date_from, date_to):
    qc_results = get_model("qc.result").search([
        ["date", ">=", date_from],
        ["date", "<=", date_to]
    ])

    total = len(qc_results)
    accepted = 0
    rejected = 0
    test_failures = {f"test{i}": 0 for i in range(1, 9)}

    for result in get_model("qc.result").browse(qc_results):
        if result.result == "accept":
            accepted += 1
        else:
            rejected += 1

        # Count test failures
        for i in range(1, 9):
            test_field = f"test{i}"
            test_value = getattr(result, test_field, None)
            if test_value == "0":
                test_failures[test_field] += 1

    acceptance_rate = (accepted / total * 100) if total > 0 else 0

    print(f"QC Results from {date_from} to {date_to}:")
    print(f"  Total inspections: {total}")
    print(f"  Accepted: {accepted} ({acceptance_rate:.1f}%)")
    print(f"  Rejected: {rejected} ({100-acceptance_rate:.1f}%)")
    print(f"\nTest Failure Breakdown:")
    for test, count in test_failures.items():
        if count > 0:
            print(f"  {test}: {count} failures")

Use Case 6: Inspector Performance Report

# Track inspector activity and accuracy
def inspector_performance_report(inspector_id, date_from, date_to):
    results = get_model("qc.result").search([
        ["inspector_id", "=", inspector_id],
        ["date", ">=", date_from],
        ["date", "<=", date_to]
    ])

    inspector = get_model("base.user").browse(inspector_id)
    qc_records = get_model("qc.result").browse(results)

    total_inspections = len(qc_records)
    accepted = sum(1 for r in qc_records if r.result == "accept")
    rejected = sum(1 for r in qc_records if r.result == "reject")

    print(f"Inspector: {inspector.name}")
    print(f"Period: {date_from} to {date_to}")
    print(f"Total Inspections: {total_inspections}")
    print(f"Accepted: {accepted}")
    print(f"Rejected: {rejected}")
    print(f"Rejection Rate: {(rejected/total_inspections*100):.1f}%")

Understanding Test Fields

The model provides 8 generic test fields (test1-test8) that can be configured to represent specific quality checks:

Example Test Configurations

Manufacturing Company: - test1: Dimensional Accuracy - test2: Visual Inspection - test3: Functional Test - test4: Performance Verification - test5: Safety Check - test6: Packaging Quality - test7: Documentation Complete - test8: Labeling Correct

Food Processing: - test1: Temperature Check - test2: Visual Appearance - test3: Taste Test - test4: Texture Evaluation - test5: Color Match - test6: Packaging Seal - test7: Expiry Date Check - test8: Barcode Verification

Electronics: - test1: Power-On Test - test2: Functional Test - test3: Calibration Check - test4: Signal Quality - test5: Physical Inspection - test6: Firmware Version - test7: Packaging Condition - test8: Documentation


Best Practices

1. Consistent Test Definitions

# Define test meanings in documentation or settings
QC_TEST_DEFINITIONS = {
    "test1": "Dimensional Accuracy",
    "test2": "Visual Inspection",
    "test3": "Functional Test",
    "test4": "Performance Test",
    "test5": "Safety Check",
    "test6": "Packaging Quality",
    "test7": "Documentation",
    "test8": "Labeling"
}

# Use consistently across all inspections

2. Determine Result from Tests

# Automatically set result based on test outcomes
def create_qc_with_auto_result(vals):
    test_results = [
        vals.get(f"test{i}") 
        for i in range(1, 9) 
        if vals.get(f"test{i}") is not None
    ]

    # If any test failed, mark as reject
    if "0" in test_results:
        vals["result"] = "reject"
    else:
        vals["result"] = "accept"

    return get_model("qc.result").create(vals)

3. Require Inspector for All Results

# Validate inspector is set
def create_qc_validated(vals):
    if not vals.get("inspector_id"):
        raise Exception("Inspector must be specified for QC result")

    return get_model("qc.result").create(vals)

# Require photo for rejected items
def create_qc_with_photo_check(vals):
    if vals.get("result") == "reject" and not vals.get("image"):
        # Warning or requirement
        print("Warning: Photo recommended for rejected items")

    return get_model("qc.result").create(vals)

Search Functions

Search by Result Type

# Find all rejected items
rejected_ids = get_model("qc.result").search([
    ["result", "=", "reject"]
])

Search by Product

# Find all QC results for a specific product
product_qc_ids = get_model("qc.result").search([
    ["product_id", "=", product_id]
])

Search by Date Range

# Find QC results in date range
date_range_ids = get_model("qc.result").search([
    ["date", ">=", "2025-01-01"],
    ["date", "<=", "2025-12-31"]
])

Search by Inspector

# Find all inspections by specific inspector
inspector_qc_ids = get_model("qc.result").search([
    ["inspector_id", "=", inspector_id]
])

Search by Lot

# Find QC results for specific lot
lot_qc_ids = get_model("qc.result").search([
    ["lot_id", "=", lot_id]
])

Integration Points

Stock Picking Integration

QC results can be linked to stock pickings (receiving, delivery, internal transfers) to ensure quality checks are performed and recorded.

Production Order Integration

QC results can be linked to production orders to verify output quality before releasing finished goods.

Stock Move Integration

The default_get method provides seamless integration with stock moves, allowing QC results to be created directly from movement screens.


Workflow Integration

QC results can trigger workflow actions:

# Example workflow triggers (if implemented)
def on_qc_result_create(qc_result):
    if qc_result.result == "reject":
        # Trigger rejection workflow
        # - Hold stock movement
        # - Notify quality manager
        # - Create corrective action
        pass
    elif qc_result.result == "accept":
        # Trigger acceptance workflow
        # - Release stock movement
        # - Update inventory
        pass

Performance Tips

1. Use Batch Processing for Multiple Inspections

# Process multiple QC results efficiently
def batch_create_qc_results(qc_data_list):
    qc_ids = []
    for qc_data in qc_data_list:
        qc_id = get_model("qc.result").create(qc_data)
        qc_ids.append(qc_id)
    return qc_ids

2. Index Frequently Searched Fields

The model already marks key fields as searchable (date, product_id, lot_id, result, inspector_id). Ensure database indexes exist for optimal performance.


Troubleshooting

"QC result not auto-populating from move"

Cause: Missing or incorrect move_id in context
Solution: Ensure context contains {"move_id": <valid_move_id>} when creating

Cause: on_delete="cascade" means QC results are deleted when parent is deleted
Solution: - This is intended behavior for data integrity - Export QC results before deleting parent records if history is needed

"Test results not affecting final result"

Cause: No automatic logic linking test outcomes to final result
Solution: Implement custom logic to auto-set result based on test values


Security Considerations

Permission Model

Consider implementing permissions for: - Creating QC results (QC inspectors) - Reviewing QC results (QC managers) - Modifying QC results (limited or audit-only) - Deleting QC results (restricted)

Data Integrity

  • QC results should generally be immutable after creation
  • Changes should be audited
  • Consider adding state workflow (draft → confirmed → reviewed)

Potential Enhancements

1. Add QC Reason Field

"reason_id": fields.Many2One("qc.reason", "Rejection Reason"),
"notes": fields.Text("Additional Notes")

2. Add State Workflow

"state": fields.Selection([
    ["draft", "Draft"],
    ["confirmed", "Confirmed"],
    ["reviewed", "Reviewed"]
], "Status")

3. Add Corrective Actions

"corrective_action": fields.Text("Corrective Action Required"),
"action_completed": fields.Boolean("Action Completed")

4. Add Statistical Fields

"defect_rate": fields.Decimal("Defect Rate %", function="calc_defect_rate"),
"pass_rate": fields.Decimal("Pass Rate %", function="calc_pass_rate")

Version History

Last Updated: October 2025
Model Version: qc_result.py
Framework: Netforce
Copyright: 2012-2015 Netforce Co. Ltd. (MIT License)


Additional Resources

  • QC Reason Documentation: qc.reason
  • Stock Picking Documentation: stock.picking
  • Production Order Documentation: production.order
  • Stock Move Documentation: stock.move

This documentation is generated for developer onboarding and reference purposes.