first commit - Week 1: C2C Odoo ERP base setup with multi-company, UoM engine, and core modules
This commit is contained in:
commit
137eb17d4f
20
.gitignore
vendored
Normal file
20
.gitignore
vendored
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
# Docker volumes - don't commit database data
|
||||||
|
.data/
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
*.pyo
|
||||||
|
|
||||||
|
# Odoo compiled assets
|
||||||
|
/addons/**/static/lib/
|
||||||
|
/addons/**/*.pyc
|
||||||
|
|
||||||
|
# Temp scripts
|
||||||
|
check_removal.py
|
||||||
|
verify_week1.py
|
||||||
|
verify.log
|
||||||
|
uom_bad.log
|
||||||
|
verify_error.log
|
||||||
|
config_odoo.py
|
||||||
2
addons/c2c_core/__init__.py
Normal file
2
addons/c2c_core/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import models
|
||||||
24
addons/c2c_core/__manifest__.py
Normal file
24
addons/c2c_core/__manifest__.py
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': 'C2C Core',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Base',
|
||||||
|
'summary': 'Core configuration for C2C Agricorp implementation',
|
||||||
|
'description': """
|
||||||
|
- Configures product and partner categories.
|
||||||
|
- Adds fields for shelf life, expiry, and quality grades.
|
||||||
|
- Enables tracking and removal strategies.
|
||||||
|
""",
|
||||||
|
'author': 'Antigravity',
|
||||||
|
'depends': ['stock', 'purchase', 'sale_management', 'mrp', 'product_expiry'],
|
||||||
|
'data': [
|
||||||
|
'data/partner_data.xml',
|
||||||
|
'data/product_category_data.xml',
|
||||||
|
'data/stock_location_data.xml',
|
||||||
|
'views/product_template_views.xml',
|
||||||
|
'views/res_partner_views.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': True,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
19
addons/c2c_core/data/partner_data.xml
Normal file
19
addons/c2c_core/data/partner_data.xml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Standard Odoo categories for farmers, wholesalers, etc. -->
|
||||||
|
<record id="res_partner_category_farmer" model="res.partner.category">
|
||||||
|
<field name="name">Farmers</field>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_wholesaler" model="res.partner.category">
|
||||||
|
<field name="name">Wholesalers</field>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_retail" model="res.partner.category">
|
||||||
|
<field name="name">Retail Customers</field>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_logistics" model="res.partner.category">
|
||||||
|
<field name="name">Logistics Providers</field>
|
||||||
|
</record>
|
||||||
|
<record id="res_partner_category_intercompany" model="res.partner.category">
|
||||||
|
<field name="name">Intercompany Partners</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
17
addons/c2c_core/data/product_category_data.xml
Normal file
17
addons/c2c_core/data/product_category_data.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="product_category_raw" model="product.category">
|
||||||
|
<field name="name">Raw Materials</field>
|
||||||
|
<field name="removal_strategy_id" ref="product_expiry.removal_fefo"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_category_semi" model="product.category">
|
||||||
|
<field name="name">Semi-finished Goods</field>
|
||||||
|
<field name="removal_strategy_id" ref="product_expiry.removal_fefo"/>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_category_finished" model="product.category">
|
||||||
|
<field name="name">Finished Goods</field>
|
||||||
|
<field name="removal_strategy_id" ref="product_expiry.removal_fefo"/>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
20
addons/c2c_core/data/stock_location_data.xml
Normal file
20
addons/c2c_core/data/stock_location_data.xml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- We need to create locations for each company?
|
||||||
|
Or just general ones for the active company?
|
||||||
|
Multi-company setup requires company_id usually.
|
||||||
|
Let's create generic ones if possible or use a script.
|
||||||
|
-->
|
||||||
|
<record id="stock_location_qc" model="stock.location">
|
||||||
|
<field name="name">QC Hold</field>
|
||||||
|
<field name="location_id" ref="stock.stock_location_locations"/>
|
||||||
|
<field name="usage">internal</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="stock_location_rejected" model="stock.location">
|
||||||
|
<field name="name">Rejected/Scrap</field>
|
||||||
|
<field name="location_id" ref="stock.stock_location_locations"/>
|
||||||
|
<field name="usage">inventory</field>
|
||||||
|
<field name="scrap_location">True</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
2
addons/c2c_core/models/__init__.py
Normal file
2
addons/c2c_core/models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import product_template
|
||||||
25
addons/c2c_core/models/product_template.py
Normal file
25
addons/c2c_core/models/product_template.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api
|
||||||
|
|
||||||
|
class ProductTemplate(models.Model):
|
||||||
|
_inherit = 'product.template'
|
||||||
|
|
||||||
|
quality_grade = fields.Selection([
|
||||||
|
('a', 'Grade A'),
|
||||||
|
('b', 'Grade B'),
|
||||||
|
('c', 'Grade C'),
|
||||||
|
], string='Quality Grade', default='a')
|
||||||
|
|
||||||
|
# Many expiry fields are in product.template if product_expiry is installed
|
||||||
|
# shelf life (expiration time in days), etc.
|
||||||
|
|
||||||
|
class ResPartner(models.Model):
|
||||||
|
_inherit = 'res.partner'
|
||||||
|
|
||||||
|
partner_category_type = fields.Selection([
|
||||||
|
('farmer', 'Farmer'),
|
||||||
|
('wholesaler', 'Wholesaler'),
|
||||||
|
('retail_customer', 'Retail Customer'),
|
||||||
|
('logistics', 'Logistics'),
|
||||||
|
('intercompany', 'Intercompany'),
|
||||||
|
], string='C2C Partner Category')
|
||||||
14
addons/c2c_core/views/product_template_views.xml
Normal file
14
addons/c2c_core/views/product_template_views.xml
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="product_template_form_view_inherit_c2c" model="ir.ui.view">
|
||||||
|
<field name="name">product.template.form.inherit.c2c</field>
|
||||||
|
<field name="model">product.template</field>
|
||||||
|
<field name="inherit_id" ref="product.product_template_form_view"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<field name="categ_id" position="after">
|
||||||
|
<field name="quality_grade"/>
|
||||||
|
</field>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
</odoo>
|
||||||
13
addons/c2c_core/views/res_partner_views.xml
Normal file
13
addons/c2c_core/views/res_partner_views.xml
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="res_partner_view_form_inherit_c2c" model="ir.ui.view">
|
||||||
|
<field name="name">res.partner.form.inherit.c2c</field>
|
||||||
|
<field name="model">res.partner</field>
|
||||||
|
<field name="inherit_id" ref="base.view_partner_form"/>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<xpath expr="//field[@name='category_id']" position="after">
|
||||||
|
<field name="partner_category_type"/>
|
||||||
|
</xpath>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
2
addons/c2c_uom_engine/__init__.py
Normal file
2
addons/c2c_uom_engine/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import models
|
||||||
21
addons/c2c_uom_engine/__manifest__.py
Normal file
21
addons/c2c_uom_engine/__manifest__.py
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
{
|
||||||
|
'name': 'C2C UoM Engine',
|
||||||
|
'version': '1.0',
|
||||||
|
'category': 'Inventory/Inventory',
|
||||||
|
'summary': 'Multi-stage UoM conversion framework for Clickstocart',
|
||||||
|
'description': """
|
||||||
|
Stores conversion rules per product category/category.
|
||||||
|
Provides a server-side conversion utility for complex UoM constraints.
|
||||||
|
""",
|
||||||
|
'author': 'Antigravity',
|
||||||
|
'depends': ['stock'],
|
||||||
|
'data': [
|
||||||
|
'security/ir.model.access.csv',
|
||||||
|
'data/uom_data.xml',
|
||||||
|
'views/uom_conversion_rule_views.xml',
|
||||||
|
],
|
||||||
|
'installable': True,
|
||||||
|
'application': False,
|
||||||
|
'license': 'LGPL-3',
|
||||||
|
}
|
||||||
25
addons/c2c_uom_engine/data/uom_data.xml
Normal file
25
addons/c2c_uom_engine/data/uom_data.xml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<!-- Weight Categories - Use existing or new? Standard typically has 'Weight' -->
|
||||||
|
<!-- Let's ensure these specific ones exist -->
|
||||||
|
<record id="product_uom_500g" model="uom.uom">
|
||||||
|
<field name="name">500g</field>
|
||||||
|
<field name="category_id" ref="uom.product_uom_categ_kgm"/>
|
||||||
|
<field name="uom_type">smaller</field>
|
||||||
|
<field name="factor">2.0</field> <!-- 1kg / 2 = 0.5kg -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_uom_100g" model="uom.uom">
|
||||||
|
<field name="name">100g</field>
|
||||||
|
<field name="category_id" ref="uom.product_uom_categ_kgm"/>
|
||||||
|
<field name="uom_type">smaller</field>
|
||||||
|
<field name="factor">10.0</field> <!-- 1kg / 10 = 0.1kg -->
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="product_uom_bundle_3m" model="uom.uom">
|
||||||
|
<field name="name">Bundle (3m)</field>
|
||||||
|
<field name="category_id" ref="uom.uom_categ_length"/>
|
||||||
|
<field name="uom_type">bigger</field>
|
||||||
|
<field name="factor_inv">3.0</field> <!-- 1m * 3 = 3m -->
|
||||||
|
</record>
|
||||||
|
</odoo>
|
||||||
2
addons/c2c_uom_engine/models/__init__.py
Normal file
2
addons/c2c_uom_engine/models/__init__.py
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from . import uom_conversion_rule
|
||||||
32
addons/c2c_uom_engine/models/uom_conversion_rule.py
Normal file
32
addons/c2c_uom_engine/models/uom_conversion_rule.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
from odoo import models, fields, api, _
|
||||||
|
from odoo.exceptions import UserError
|
||||||
|
|
||||||
|
class UoMConversionRule(models.Model):
|
||||||
|
_name = 'c2c.uom.conversion.rule'
|
||||||
|
_description = 'C2C UoM Conversion Rule'
|
||||||
|
|
||||||
|
name = fields.Char(string='Rule Name', required=True)
|
||||||
|
product_category_id = fields.Many2one('product.category', string='Product Category')
|
||||||
|
from_uom_id = fields.Many2one('uom.uom', string='From UoM', required=True)
|
||||||
|
to_uom_id = fields.Many2one('uom.uom', string='To UoM', required=True)
|
||||||
|
ratio = fields.Float(string='Conversion Ratio', default=1.0, required=True, digits=(16, 4))
|
||||||
|
description = fields.Text(string='Description')
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def convert(self, product, quantity, from_uom, to_uom):
|
||||||
|
"""
|
||||||
|
Utility method to convert quantities based on local rules if they exist.
|
||||||
|
Otherwise falls back to standard Odoo UoM conversion.
|
||||||
|
"""
|
||||||
|
rule = self.search([
|
||||||
|
('product_category_id', '=', product.categ_id.id),
|
||||||
|
('from_uom_id', '=', from_uom.id),
|
||||||
|
('to_uom_id', '=', to_uom.id)
|
||||||
|
], limit=1)
|
||||||
|
|
||||||
|
if rule:
|
||||||
|
return quantity * rule.ratio
|
||||||
|
|
||||||
|
# Fallback to Odoo standard
|
||||||
|
return from_uom._compute_quantity(quantity, to_uom)
|
||||||
2
addons/c2c_uom_engine/security/ir.model.access.csv
Normal file
2
addons/c2c_uom_engine/security/ir.model.access.csv
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
|
||||||
|
access_c2c_uom_conversion_rule,c2c.uom.conversion.rule,model_c2c_uom_conversion_rule,base.group_user,1,1,1,1
|
||||||
|
34
addons/c2c_uom_engine/views/uom_conversion_rule_views.xml
Normal file
34
addons/c2c_uom_engine/views/uom_conversion_rule_views.xml
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<odoo>
|
||||||
|
<record id="view_c2c_uom_conversion_rule_tree" model="ir.ui.view">
|
||||||
|
<field name="name">c2c.uom.conversion.rule.tree</field>
|
||||||
|
<field name="model">c2c.uom.conversion.rule</field>
|
||||||
|
<field name="arch" type="xml">
|
||||||
|
<tree string="UoM Conversion Rules" editable="bottom">
|
||||||
|
<field name="name"/>
|
||||||
|
<field name="product_category_id"/>
|
||||||
|
<field name="from_uom_id"/>
|
||||||
|
<field name="to_uom_id"/>
|
||||||
|
<field name="ratio"/>
|
||||||
|
<field name="description"/>
|
||||||
|
</tree>
|
||||||
|
</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<record id="action_c2c_uom_conversion_rule" model="ir.actions.act_window">
|
||||||
|
<field name="name">Conversion Rules</field>
|
||||||
|
<field name="res_model">c2c.uom.conversion.rule</field>
|
||||||
|
<field name="view_mode">list,form</field>
|
||||||
|
</record>
|
||||||
|
|
||||||
|
<menuitem id="menu_c2c_uom_engine"
|
||||||
|
name="UoM Engine"
|
||||||
|
parent="stock.menu_stock_config"
|
||||||
|
sequence="100"/>
|
||||||
|
|
||||||
|
<menuitem id="menu_c2c_uom_conversion_rule"
|
||||||
|
name="Conversion Rules"
|
||||||
|
parent="menu_c2c_uom_engine"
|
||||||
|
action="action_c2c_uom_conversion_rule"
|
||||||
|
sequence="10"/>
|
||||||
|
</odoo>
|
||||||
33
docker-compose.yml
Normal file
33
docker-compose.yml
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:15
|
||||||
|
container_name: odoo_clickstocart_db
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: postgres
|
||||||
|
POSTGRES_USER: odoo
|
||||||
|
POSTGRES_PASSWORD: odoo
|
||||||
|
volumes:
|
||||||
|
- client1_pgdata:/var/lib/postgresql/data
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
odoo:
|
||||||
|
image: odoo:17.0
|
||||||
|
container_name: odoo_clickstocart
|
||||||
|
depends_on:
|
||||||
|
- db
|
||||||
|
ports:
|
||||||
|
- "10005:8069"
|
||||||
|
environment:
|
||||||
|
HOST: db
|
||||||
|
USER: odoo
|
||||||
|
PASSWORD: odoo
|
||||||
|
volumes:
|
||||||
|
- client1_odoo_data:/var/lib/odoo
|
||||||
|
- ./addons:/mnt/extra-addons
|
||||||
|
restart: always
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
client1_pgdata:
|
||||||
|
client1_odoo_data:
|
||||||
84
weekone.md
Normal file
84
weekone.md
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
# Week 1 Implementation Guide & Verification Manual
|
||||||
|
|
||||||
|
This document is your complete step-by-step guide on how to manually verify and test all the Week 1 features directly within the Odoo user interface.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. How to Check: Multi-Company Architecture
|
||||||
|
**Goal**: Verify that all 3 companies and their isolated warehouses exist.
|
||||||
|
|
||||||
|
**Instructions**:
|
||||||
|
1. Login to `NewClickstoCart` as the administrator.
|
||||||
|
2. Look at the top-right corner of the top navigation bar. Click on the **Company Switcher** (the building icon next to your profile picture).
|
||||||
|
3. **Verify:** You should see checkboxes for `C2C Agricorp India Pvt Ltd`, `C2C Imports & Exports`, and `Clickstocart Inc`.
|
||||||
|
4. Click on the **Settings app** > **Users & Companies** > **Companies**.
|
||||||
|
5. **Verify:** The 3 companies are listed here with their respective details.
|
||||||
|
6. Still in Settings, navigate to **Users & Companies** > **Groups**.
|
||||||
|
7. **Verify:** Search for "Manufacturing Manager", "Warehouse Staff", etc. You will see these roles exist for assigning to future employees.
|
||||||
|
8. Open the **Inventory app** > **Configuration** > **Warehouses**.
|
||||||
|
9. **Verify:** You should see one distinct warehouse per company (e.g., `C2C - C2C Agricorp`, `C2C - Clickstocart`).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. How to Check: Master Data Fields (Products & Partners)
|
||||||
|
**Goal**: Verify custom fields and categorized data templates.
|
||||||
|
|
||||||
|
**Instructions (Partners/Contacts)**:
|
||||||
|
1. Open the **Contacts app**.
|
||||||
|
2. Click **New** to create a test contact.
|
||||||
|
3. In the form, look closely below the tags/categories area or in the main tabs.
|
||||||
|
4. **Verify:** You will see a new dropdown field called **C2C Partner Category**. Click it to ensure options like `Farmer`, `Wholesaler`, `Retail Customer` exist.
|
||||||
|
|
||||||
|
**Instructions (Products)**:
|
||||||
|
1. Open the **Inventory app** > **Products** > **Products**.
|
||||||
|
2. Click **New**.
|
||||||
|
3. Under the **General Information** page, click the **Product Category** dropdown.
|
||||||
|
4. **Verify:** You will see categories explicitly created for us: `Raw Materials`, `Semi-finished Goods`, and `Finished Goods`.
|
||||||
|
5. Still on the product form, look at the bottom of the main fields.
|
||||||
|
6. **Verify:** There is a new dropdown called **Quality Grade** containing `Grade A`, `Grade B`, `Grade C`.
|
||||||
|
7. Go to the **Inventory tab** on the product form.
|
||||||
|
8. **Verify:** Under **Traceability**, ensure the **By Lots** option is available. Check the **Expiration Date** box to see the `Shelf Life` fields appear.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. How to Check: UoM Engine
|
||||||
|
**Goal**: Test the new complex conversion engine and the new standard units.
|
||||||
|
|
||||||
|
**Instructions**:
|
||||||
|
1. Open the **Inventory app**.
|
||||||
|
2. Navigate to **Configuration** > **Units of Measure** (Check 'UoM Categories' if 'Units of Measure' isn't directly visible).
|
||||||
|
3. **Verify:** Search for `500g`, `100g`, or `Bundle (3m)`. They are fully registered in the standard weight/length systems.
|
||||||
|
4. Navigate to **Configuration** > **Conversion Rules** (Under the new "UoM Engine" section).
|
||||||
|
5. **Verify:** This is the new custom dashboard. Click **New**. You can now define a rule that says "For Category X, converting from Y to Z uses Ratio R".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. How to Check: Batch/Expiry & QC Workflow
|
||||||
|
**Goal**: Verify that Odoo enforces FEFO (First Expired First Out) and has a location to hold quality checks.
|
||||||
|
|
||||||
|
**Instructions (QC Locations)**:
|
||||||
|
1. In the **Inventory app**, turn on Developer Mode if you can't see the Locations menu.
|
||||||
|
2. Go to **Configuration** > **Locations**.
|
||||||
|
3. **Verify:** Remove the "Internal" filter in the search bar and search for `QC Hold`. You will see it exists as an Internal Location.
|
||||||
|
4. **Verify:** Search for `Rejected/Scrap`. It exists and has the flag "Is a Scrap Location" checked.
|
||||||
|
|
||||||
|
**Instructions (FEFO Strategy)**:
|
||||||
|
1. Go to **Inventory app** > **Configuration** > **Product Categories**.
|
||||||
|
2. Open the `Raw Materials` category.
|
||||||
|
3. **Verify:** Scroll down to the Logistics section. The **Force Removal Strategy** is set to `First Expired, First Out (FEFO)`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. End-to-End Test: The Farmer Procurement Flow
|
||||||
|
To see everything working together, perform this exact sequence:
|
||||||
|
|
||||||
|
1. **Prep**: Create a Product named "Test Turmeric", set tracking to "By Lots", and check "Expiration Date" (give it 100 days shelf life).
|
||||||
|
2. **Purchase**: Open the **Purchase app**. Select a vendor/Farmer. Add "Test Turmeric" and click **Confirm Order**.
|
||||||
|
3. **Receipt**: Click the **Receive Products** button in the top right of the Purchase Order.
|
||||||
|
4. **Create Lot**: On the receipt line for Turmeric, you cannot validate yet. Click the **List icon (Options/Details)** on the right side of the product line.
|
||||||
|
5. Under "Assign Serial/Lot Number", type in `LOT-001` and set an Expiration Date for tomorrow. Apply and **Validate** the receipt.
|
||||||
|
6. **QC Transfer**: Open the **Inventory app** > **Operations** > **Transfers**. Create a new internal transfer.
|
||||||
|
7. Set the Source Location as `WH/Stock` (where the goods just arrived) and the Destination Location to `QC Hold`.
|
||||||
|
8. Add the Product, select `LOT-001`, and **Validate**.
|
||||||
|
|
||||||
|
### **Congratulations!** You have just executed a full cycle of Procurement -> Quality Control with multi-stage Lot tracking and Expiry enforcement.
|
||||||
Loading…
x
Reference in New Issue
Block a user