new update

This commit is contained in:
raman 2025-11-03 12:23:02 +05:30
parent 07818afb3e
commit 57061ad060
28 changed files with 658 additions and 28 deletions

View File

@ -6,7 +6,7 @@
'description': """ BOM Cost """, 'description': """ BOM Cost """,
'author': 'Raman Marikanti', 'author': 'Raman Marikanti',
'category': 'Manufacturing', 'category': 'Manufacturing',
'depends': ['sale_management', 'mrp','stock'], 'depends': ['sale_management', 'mrp','stock',],
'data': [ 'data': [
"views/bom_view.xml", "views/bom_view.xml",
"report/mrp_costing_report.xml", "report/mrp_costing_report.xml",

View File

@ -4,13 +4,98 @@ from odoo.tools import float_round
from odoo.exceptions import ValidationError from odoo.exceptions import ValidationError
class SaleOrderInherit(models.Model):
_inherit = 'sale.order'
freight_charges = fields.Monetary(string='Freight', readonly=False)
total_production_cost = fields.Monetary(string="Total Production Cost", readonly=True, compute="_compute_purchase_price", precompute=True)
@api.depends('freight_charges', 'order_line')
def _compute_purchase_price(self):
for order in self:
production_cost = 0
for line in order.order_line:
production_cost += line.purchase_price * line.product_uom_qty
order.total_production_cost = production_cost + order.freight_charges
#
# class AccountMoveInherit(models.Model):
# _inherit = 'account.move'
#
# freight_charges = fields.Monetary(string='Freight Charges', readonly=False, store=True,
# compute='_compute_freight_charges_amount')
#
# @api.onchange('freight_charges')
# @api.depends(
# 'invoice_line_ids.currency_rate',
# 'invoice_line_ids.tax_base_amount',
# 'invoice_line_ids.tax_line_id',
# 'invoice_line_ids.price_total',
# 'invoice_line_ids.price_subtotal',
# 'invoice_payment_term_id',
# 'partner_id',
# 'currency_id',
# )
# def _compute_tax_totals(self):
# """ Computed field used for custom widget's rendering.
# Only set on invoices.
# """
# res = super()._compute_tax_totals()
# for order in self:
# order.tax_totals['subtotals'][0]['base_amount'] += order.freight_charges
# order.tax_totals['subtotals'][0]['base_amount_currency'] += order.freight_charges
# order.tax_totals['base_amount'] += order.freight_charges
# order.tax_totals['base_amount_currency'] += order.freight_charges
# order.tax_totals['total_amount'] += order.freight_charges
# order.tax_totals['total_amount_currency'] += order.freight_charges
# return res
class SaleOrderLine(models.Model): class SaleOrderLine(models.Model):
_inherit = 'sale.order.line' _inherit = 'sale.order.line'
unit_prod_cost = fields.Float('Unit Prodcut Cost',digits="Product Unit of Measure")
@api.depends('product_id') purchase_price = fields.Float(
def _compute_unit_prod_cost(self): string="Cost", compute="_compute_purchase_price",
digits='Product Price', store=True, readonly=False, copy=False, precompute=True,
groups="base.group_user")
bag_weight = fields.Float(
string="Weight", compute="_compute_bag_weight",
digits='Product Price', store=True, readonly=False, precompute=True,
groups="base.group_user")
@api.depends('product_id', 'company_id', 'product_uom')
def _compute_bag_weight(self):
for line in self: for line in self:
line.unit_prod_cost = line.product_id.standard_price line.bag_weight = line.product_id.weight * line.product_uom_qty
@api.depends('product_id', 'company_id', 'currency_id', 'product_uom')
def _compute_purchase_price(self):
for line in self:
if not line.product_id:
line.purchase_price = 0.0
continue
line = line.with_company(line.company_id)
# Convert the cost to the line UoM
product_cost = line.product_id.uom_id._compute_price(
line.product_id.standard_price,
line.product_uom,
)
line.purchase_price = line._convert_to_sol_currency(
product_cost,
line.product_id.cost_currency_id)
@api.depends('product_id', 'product_uom', 'product_uom_qty')
def _compute_price_unit(self):
for line in self:
line.price_unit = 0.0

View File

@ -14,6 +14,47 @@
</xpath> </xpath>
</field> </field>
</record> </record>
<!-- Inherit sale invoice Form View for freight_charges field addition -->
<record id="sale_order_form_inherit" model="ir.ui.view">
<field name="name">sale.order.form.inherit</field>
<field name="model">sale.order</field>
<field name="inherit_id" ref="sale.view_order_form"/>
<field name="arch" type="xml">
<xpath expr="//field[@name='order_line']/list//field[@name='price_unit']" position="after">
<field name="purchase_price" groups="base.group_user"/>
<field name="bag_weight" groups="base.group_user"/>
</xpath>
<xpath expr="//field[@name='tax_totals']" position="before">
<div class="d-flex float-end" colspan="2" groups="base.group_user">
<label for="freight_charges"/>
<div>
<field name="freight_charges" class="oe_inline" force_save="1" widget="monetary" options="{'currency_field': 'currency_id'}"/>
</div>
</div>
<div class="d-flex float-end" colspan="2" groups="base.group_user">
<label for="total_production_cost"/>
<div>
<field name="total_production_cost" class="oe_inline" force_save="1" widget="monetary" options="{'currency_field': 'currency_id'}"/>
</div>
</div>
</xpath>
</field>
</record>
<!-- <record id="account_move_form_inherit" model="ir.ui.view">-->
<!-- <field name="name">account.move.form.inherit</field>-->
<!-- <field name="model">account.move</field>-->
<!-- <field name="inherit_id" ref="account.view_move_form"/>-->
<!-- <field name="arch" type="xml">-->
<!-- <xpath expr="//field[@name='tax_totals']" position="before">-->
<!-- <field name="freight_charges" force_save="1" string="Freight" widget="monetary" options="{'currency_field': 'currency_id'}"/>-->
<!-- </xpath>-->
<!-- </field>-->
<!-- </record>-->
<record id="mrp_production_form_view_decoration" model="ir.ui.view"> <record id="mrp_production_form_view_decoration" model="ir.ui.view">
<field name="name">mrp.production.inherited.form.decoration</field> <field name="name">mrp.production.inherited.form.decoration</field>
<field name="model">mrp.production</field> <field name="model">mrp.production</field>

View File

@ -2,6 +2,7 @@
'name': 'Samashti Dashboard (OWL)', 'name': 'Samashti Dashboard (OWL)',
'version': '1.0', 'version': '1.0',
'category': 'View', 'category': 'View',
'license': 'LGPL-3',
'summary': 'Samashti Dashboard (OWL + pqGrid)', 'summary': 'Samashti Dashboard (OWL + pqGrid)',
'author': 'Raman Marikanti', 'author': 'Raman Marikanti',
'depends': ['stock', 'web_grid','costing_mrp_bom'], 'depends': ['stock', 'web_grid','costing_mrp_bom'],

View File

@ -112,6 +112,39 @@ class SamashtiDashboard(models.AbstractModel):
def get_sale_margin_data(self,from_date,to_date): def get_sale_margin_data(self,from_date,to_date):
fromDate = "'"+str(from_date)+" 00:00:00'" fromDate = "'"+str(from_date)+" 00:00:00'"
toDate = "'"+str(to_date)+" 23:59:59'" toDate = "'"+str(to_date)+" 23:59:59'"
datas = []
sale_orders = self.env['sale.order'].search_read(
[
('state', '=', 'sale'),
('date_order', '>=', fromDate),
('date_order', '<=', toDate)
],
['id', 'name', 'amount_total', 'partner_id', 'total_production_cost','date_order']
)
for r in sale_orders:
cost = r['total_production_cost']
sale_price = r['amount_total']
margin = sale_price - cost
margin_percent = (margin / sale_price * 100) if sale_price else 0.0
customer = r['partner_id'][-1]
quantity = sum(self.env['sale.order'].browse(r['id']).order_line.mapped('product_uom_qty'))
weight = str(sum(self.env['sale.order'].browse(r['id']).order_line.mapped('bag_weight')))+" kg"
datas.append({
'sale_order': r['name'],
'weight':weight,
'customer':customer,
'quantity':quantity,
'cost': cost,
'date':r['date_order'],
'sale_price': sale_price,
'margin': margin,
'margin_percent':margin_percent
})
return datas
where_caluse = f" AND so.date_order BETWEEN {fromDate} AND {toDate}" where_caluse = f" AND so.date_order BETWEEN {fromDate} AND {toDate}"
sql = """ sql = """
SELECT SELECT
@ -152,3 +185,17 @@ class SamashtiDashboard(models.AbstractModel):
else: else:
return [] return []
def _get_production_cost(self, sale_order_id):
sale_order = self.env['sale.order'].browse(sale_order_id)
cost = 0.0
for line in sale_order.order_line.filtered(lambda l: l.product_id.type == 'consu'):
line_cost = line.purchase_price or 0
cost += line_cost * line.product_uom_qty
for line in sale_order.order_line.filtered(lambda l: l.product_id.type == 'service' and l.price_total > 0):
cost += line.price_total
return cost

View File

@ -172,16 +172,14 @@ export class SamashtiDashboard extends Component {
async getSaleColumns(){ async getSaleColumns(){
return[ return[
{ title: "Sale Order", dataIndx: "sale_order", width: 100,filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, { title: "Sale Order", dataIndx: "sale_order", width: 100,filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } },
{ title: "Product Code", dataIndx: "product_code", width: 100,filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, { title: "Customer", dataIndx: "customer", width: 280,filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } },
{ title: "Product Name", dataIndx: "product_name", width: 280,filter: { type: 'textbox', condition: 'contain', listeners: ['keyup'] } }, { title: "Date", dataIndx: "date", width: 150 },
{ title: "Category", dataIndx: "category", width: 150 },
{ title: "Quantity", dataIndx: "quantity", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" }, { title: "Quantity", dataIndx: "quantity", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" },
{ title: "Unit Cost", dataIndx: "unit_cost", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" }, { title: "Weight", dataIndx: "weight", width: 150 },
{ title: "Unit Sale Price", dataIndx: "unit_sale_price", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" }, { title: "Production Cost", dataIndx: "cost", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" },
{ title: "Unit Margin", dataIndx: "margin", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" }, { title: "Sale Price", dataIndx: "sale_price", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" },
{ title: "Total Cost", dataIndx: "total_cost", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" }, { title: "Margin", dataIndx: "margin", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" },
{ title: "Total Sale Price", dataIndx: "total_sale_price", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" }, { title: "Margin %", dataIndx: "margin_percent", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" },
{ title: "Total Margin", dataIndx: "total_margin", width: 120, dataType: "float", format: "#,###.00",summary: { type: "sum" },align: "right" },
] ]
} }

View File

@ -0,0 +1 @@
from . import models

View File

@ -0,0 +1,22 @@
{
'name': 'Asset Management',
'version': '18.0',
'category': 'Documents',
'summary': 'Fixed Asset Management',
'author': 'Raman Marikanti',
'depends': ['mail', 'base'],
'data': [
'data/ir_sequence.xml',
'views/asset_category_view.xml',
'views/asset_detail_view.xml',
'views/asset_location_view.xml',
'views/asset_move_view.xml',
'security/ir.model.access.csv',
],
'license': 'LGPL-3',
'installable': True,
'application': True,
'auto_install': False,
}

View File

@ -0,0 +1,18 @@
<odoo>
<data noupdate="1">
<record id="sequence_asset_move" model="ir.sequence">
<field name="name">Asset Move</field>
<field name="code">asset.move</field>
<field name="prefix">MOV/%(range_year)s/</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
<record id="sequence_asset_detail" model="ir.sequence">
<field name="name">Asset Detail</field>
<field name="code">asset.detail</field>
<field name="prefix">AST/%(range_year)s/</field>
<field name="padding">5</field>
<field name="company_id" eval="False"/>
</record>
</data>
</odoo>

View File

@ -0,0 +1,4 @@
from . import asset_detail
from . import asset_move
from . import asset_location
from . import asset_category

View File

@ -0,0 +1,8 @@
from odoo import fields, models, api
class AssetCategory(models.Model):
_name = "asset.category"
_description = "Asset Category"
name = fields.Char(string="Name")

View File

@ -0,0 +1,40 @@
from odoo import fields, models, api
from datetime import datetime
class AssetDetail(models.Model):
_name = "asset.detail"
_description = "Asset Detail"
name = fields.Char(string="Name")
asset_image = fields.Binary(string="Image")
category_id = fields.Many2one(comodel_name="asset.category", string="Category")
asset_code = fields.Char(string="Asset Code")
asset_model = fields.Char(string="Asset Model")
serial_no = fields.Char(string="Serial No.")
purchase_date = fields.Date(string="Purchase Date")
purchase_value = fields.Float(string="Purchase Value")
location_id = fields.Many2one(comodel_name="asset.location", string="Current Location")
# employee_id = fields.Many2one(comodel_name="hr.employee", string="Employee")
vendor_id = fields.Many2one(comodel_name="res.partner", string="Vendor")
warranty_start = fields.Date(string="Warranty Start")
warranty_end = fields.Date(string="Warranty End")
note = fields.Html(string="Note")
state = fields.Selection([('draft', 'New'), ('active', 'Active'), ('scrap', 'Scrap')], string='State', default="draft")
@api.model
def create(self, vals):
location_id = self.env["asset.location"].search([("is_default", "=", True)], limit=1)
vals["asset_code"] = self.env["ir.sequence"].next_by_code("asset.detail", sequence_date=datetime.now().year) or "New"
vals["location_id"] = location_id.id if location_id else None
return super(AssetDetail, self).create(vals)
def scrap_asset(self):
for asset_id in self:
location_id = self.env["asset.location"].search([("is_scrap", "=", True)], limit=1)
if location_id:
asset_id.state = "scrap"
def confirm_asset(self):
for asset_id in self:
asset_id.state = "active"

View File

@ -0,0 +1,12 @@
from odoo import fields, models, api
class AssetLocation(models.Model):
_name = "asset.location"
_description = "Asset Location"
name = fields.Char(string="Name")
is_default = fields.Boolean(string="Default")
is_scrap = fields.Boolean(string="Scrap")
asset_line = fields.One2many(comodel_name="asset.detail", inverse_name="location_id")

View File

@ -0,0 +1,31 @@
from odoo import fields, models, api
from datetime import datetime
class AssetMove(models.Model):
_name = "asset.move"
_description = "Asset Move"
name = fields.Char(string="Name")
location_id = fields.Many2one(comodel_name="asset.location", string="Source Location")
location_dest_id = fields.Many2one(comodel_name="asset.location", string="Destination Location")
asset_id = fields.Many2one(comodel_name="asset.detail", string="Asset")
state = fields.Selection([('draft', 'Draft'),('done', 'Done'),('cancel', 'Cancel')], string='State', default="draft")
@api.model
def create(self, vals):
vals["name"] = self.env["ir.sequence"].next_by_code("asset.move", sequence_date=datetime.now().year) or "New"
return super(AssetMove, self).create(vals)
@api.onchange("asset_id")
def onchange_asset(self):
self.location_id = self.asset_id.location_id.id
def move_asset(self):
for asset_id in self:
asset_id.asset_id.location_id = asset_id.location_dest_id.id
asset_id.state = "done"
def cancel_move(self):
for asset_id in self:
asset_id.state = "cancel"

View File

@ -0,0 +1,5 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
asset_category,asset.category,model_asset_category,,1,1,1,1
asset_detail,asset.detail,model_asset_detail,,1,1,1,1
asset_location,asset.location,model_asset_location,,1,1,1,1
asset_move,asset.move,model_asset_move,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 asset_category asset.category model_asset_category 1 1 1 1
3 asset_detail asset.detail model_asset_detail 1 1 1 1
4 asset_location asset.location model_asset_location 1 1 1 1
5 asset_move asset.move model_asset_move 1 1 1 1

View File

@ -0,0 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:31255c2b3d454708d62a118e2a9b9e7526670ad0f5774acec8fb83db71d6b8fc
size 43161

View File

@ -0,0 +1,19 @@
<odoo>
<record id="asset_category_list_view" model="ir.ui.view">
<field name="name">Asset Category</field>
<field name="model">asset.category</field>
<field name="arch" type="xml">
<list editable="top">
<field name="name"/>
</list>
</field>
</record>
<record id="action_asset_category" model="ir.actions.act_window">
<field name="name">Asset Category</field>
<field name="res_model">asset.category</field>
<field name="view_mode">list</field>
</record>
<menuitem id="main_menu_assets" name="Asset Management"/>
<menuitem id="menu_asset_configuration" name="Configuration" parent="main_menu_assets" sequence="2"/>
<menuitem id="menu_asset_category" action="action_asset_category" parent="menu_asset_configuration"/>
</odoo>

View File

@ -0,0 +1,68 @@
<odoo>
<record id="asset_detail_list_view" model="ir.ui.view">
<field name="name">asset.detail.list.view</field>
<field name="model">asset.detail</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
<field name="category_id"/>
<field name="asset_model"/>
<field name="serial_no"/>
<field name="state"/>
</list>
</field>
</record>
<record id="asset_detail_form_view" model="ir.ui.view">
<field name="name">asset.detail.form.view</field>
<field name="model">asset.detail</field>
<field name="arch" type="xml">
<form>
<header>
<button name="confirm_asset" string="Confirm" class="btn-primary" type="object" invisible="state not in 'draft'"/>
<button name="scrap_asset" string="Scrap" class="btn-primary" type="object" invisible="state not in 'scrap'"/>
<field name="state" widget="statusbar" statusbar_visible="draft,active"/>
</header>
<sheet>
<div class="oe_title">
<label class="oe_edit_only" for="name" string="Asset Name"/>
<h1>
<field name="name" placeholder="Name"/>
</h1>
</div>
<group>
<group>
<field name="category_id" options="{'no_create_edit': True, 'no_create': True}"/>
<field name="asset_code" readonly="1"/>
<field name="asset_model"/>
<field name="serial_no"/>
</group>
<group>
<field name="purchase_date"/>
<field name="purchase_value"/>
<field name="location_id" options="{'no_create_edit': True, 'no_create': True}"/>
<!-- <field name="employee_id" options="{'no_create_edit': True, 'no_create': True}"/>-->
</group>
</group>
<notebook>
<page string="Warranty">
<group>
<group>
<field name="vendor_id" options="{'no_create_edit': True, 'no_create': True}"/>
<field name="warranty_start"/>
<field name="warranty_end"/>
</group>
</group>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_asset_detail" model="ir.actions.act_window">
<field name="name">Assets</field>
<field name="res_model">asset.detail</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="main_menu_asset" name="Asset" parent="main_menu_assets" sequence="1"/>
<menuitem id="menu_asset_detail" action="action_asset_detail" parent="main_menu_asset"/>
</odoo>

View File

@ -0,0 +1,53 @@
<odoo>
<record id="asset_location_list_view" model="ir.ui.view">
<field name="name">Asset Location</field>
<field name="model">asset.location</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
<field name="is_default"/>
<field name="is_scrap"/>
</list>
</field>
</record>
<record id="asset_location_form_view" model="ir.ui.view">
<field name="name">asset.location.form.view</field>
<field name="model">asset.location</field>
<field name="arch" type="xml">
<form>
<sheet>
<div class="oe_title">
<label class="oe_edit_only" for="name" string="Location"/>
<h1>
<field name="name" placeholder="Name"/>
</h1>
</div>
<group>
<group>
<field name="is_default"/>
<field name="is_scrap"/>
</group>
<group/>
</group>
<notebook>
<page string="Assets">
<field name="asset_line" >
<list string="Assets" editable="bottom" delete="0">
<field name="location_id" column_invisible="1" />
<field name="name"/>
<field name="asset_code"/>
</list>
</field>
</page>
</notebook>
</sheet>
</form>
</field>
</record>
<record id="action_asset_location" model="ir.actions.act_window">
<field name="name">Asset Location</field>
<field name="res_model">asset.location</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_asset_location" action="action_asset_location" parent="menu_asset_configuration"/>
</odoo>

View File

@ -0,0 +1,45 @@
<odoo>
<record id="asset_move_list_view" model="ir.ui.view">
<field name="name">asset.move.list.view</field>
<field name="model">asset.move</field>
<field name="arch" type="xml">
<list>
<field name="name"/>
<field name="asset_id"/>
<field name="location_id"/>
<field name="location_dest_id"/>
<field name="state"/>
</list>
</field>
</record>
<record id="asset_move_form_view" model="ir.ui.view">
<field name="name">asset.move.form.view</field>
<field name="model">asset.move</field>
<field name="arch" type="xml">
<form>
<header>
<button name="move_asset" string="Move" class="btn-primary" type="object" invisible="state not in 'draft'"/>
<button name="cancel_move" string="Cancel" type="object" invisible="state not in 'draft'"/>
<field name="state" widget="statusbar" statusbar_visible="draft,done"/>
</header>
<sheet>
<group>
<group>
<field name="name" placeholder="Name" readonly="1"/>
<field name="asset_id" domain="([('state', '=', 'active')])" options="{'no_create_edit': True, 'no_create': True}"/>
<field name="location_id" domain="([('is_scrap', '=', False)])" options="{'no_create_edit': True, 'no_create': True}"/>
<field name="location_dest_id" domain="([('is_scrap', '=', False)])" options="{'no_create_edit': True, 'no_create': True}"/>
</group>
<group/>
</group>
</sheet>
</form>
</field>
</record>
<record id="action_asset_move" model="ir.actions.act_window">
<field name="name">Asset Move</field>
<field name="res_model">asset.move</field>
<field name="view_mode">list,form</field>
</record>
<menuitem id="menu_asset_move" action="action_asset_move" parent="main_menu_asset"/>
</odoo>

View File

@ -4,6 +4,7 @@ from email.policy import default
from odoo import _, api, fields, models from odoo import _, api, fields, models
from odoo.exceptions import ValidationError, UserError from odoo.exceptions import ValidationError, UserError
class Grn(models.Model): class Grn(models.Model):
_name = 'grn' _name = 'grn'
_description = 'Goods Receipt Note' _description = 'Goods Receipt Note'
@ -44,8 +45,36 @@ class Grn(models.Model):
def _get_product_catalog_order_data(self, products, **kwargs): def _get_product_catalog_order_data(self, products, **kwargs):
res = super()._get_product_catalog_order_data(products, **kwargs) res = super()._get_product_catalog_order_data(products, **kwargs)
for product in products:
res[product.id] |= {
'price': product.standard_price,
}
return res return res
def _get_product_catalog_domain(self):
"""Get the domain to search for products in the catalog.
"""
domain = [('company_id', 'in', [self.company_id.id, False]), ('type', '=', 'consu'), ('purchase_ok', '=', True)]
return domain
def _update_order_line_info(self, product_id, quantity, **kwargs):
self.ensure_one()
pol = self.grn_line_ids.filtered(lambda line: line.product_id.id == product_id)
if pol:
if quantity != 0:
pol.quantity = quantity
else:
pol.unlink()
elif quantity > 0:
pol = self.env['grn.lines'].create({
'grn_id': self.id,
'product_id': product_id,
'quantity': quantity,
})
else:
pass
return 0
@api.depends('grn_line_ids') @api.depends('grn_line_ids')
def _compute_total_amount(self): def _compute_total_amount(self):
"""Compute the value of the field computed_field.""" """Compute the value of the field computed_field."""
@ -55,8 +84,12 @@ class Grn(models.Model):
amount += (line.quantity * line.price) amount += (line.quantity * line.price)
record.total_amount = amount record.total_amount = amount
def _get_product_catalog_lines_data(self):
catalog_info = {
'quantity': self.quantity,
'price': self.product_id.standard_price,
}
return catalog_info
def button_action_confirm(self): def button_action_confirm(self):
"""Confirm the GRN and assign sequence and received user""" """Confirm the GRN and assign sequence and received user"""
@ -122,7 +155,7 @@ class Grn(models.Model):
'location_dest_id': self.location_id.id, 'location_dest_id': self.location_id.id,
'location_id': picking_type.default_location_src_id.id, 'location_id': picking_type.default_location_src_id.id,
'picking_type_id': picking_type.id, 'picking_type_id': picking_type.id,
'partner_id':self.vendor_id.id 'partner_id': self.vendor_id.id
}) })
moves = [] moves = []
@ -140,7 +173,7 @@ class Grn(models.Model):
picking.action_confirm() picking.action_confirm()
for move in picking: for move in picking:
move.location_dest_id = self.location_id move.location_dest_id = self.location_id
picking.location_dest_id = self.location_id picking.location_dest_id = self.location_id
self.picking_id = picking self.picking_id = picking
def action_view_transfer(self): def action_view_transfer(self):
@ -155,7 +188,6 @@ class Grn(models.Model):
'res_id': self.picking_id.id, 'res_id': self.picking_id.id,
} }
@api.model_create_multi @api.model_create_multi
def create(self, vals_list): def create(self, vals_list):
return super().create(vals_list) return super().create(vals_list)
@ -170,10 +202,6 @@ class Grn(models.Model):
) % self.name) ) % self.name)
return super().unlink() return super().unlink()
def action_add_from_catalog(self):
res = super().action_add_from_catalog()
return res
class GrnLines(models.Model): class GrnLines(models.Model):
_name = 'grn.lines' _name = 'grn.lines'
@ -184,7 +212,7 @@ class GrnLines(models.Model):
'product.product', 'product.product',
string='Product', string='Product',
ondelete="cascade", ondelete="cascade",
domain=[('type', '!=', 'service'),('purchase_ok','=',True)], domain=[('type', '!=', 'service'), ('purchase_ok', '=', True)],
index=True index=True
) )
product_uom_id = fields.Many2one(related='product_id.uom_id', string='Unit of Measure') product_uom_id = fields.Many2one(related='product_id.uom_id', string='Unit of Measure')
@ -204,13 +232,10 @@ class GrnLines(models.Model):
if len(duplicate_lines) > 1: if len(duplicate_lines) > 1:
raise UserError(_('The product %s already exists in the GRN.') % rec.product_id.display_name) raise UserError(_('The product %s already exists in the GRN.') % rec.product_id.display_name)
def action_add_from_catalog(self): def action_add_from_catalog(self):
order = self.env['grn'].browse(self.env.context.get('order_id')) order = self.env['grn'].browse(self.env.context.get('order_id'))
return order.with_context(child_field='grn_line_ids').action_add_from_catalog() return order.with_context(child_field='grn_line_ids').action_add_from_catalog()
def unlink(self): def unlink(self):
if any(rec.grn_id.state not in ['cancel', 'draft'] for rec in self): if any(rec.grn_id.state not in ['cancel', 'draft'] for rec in self):
raise UserError(_( raise UserError(_(

View File

@ -33,9 +33,11 @@
</group> </group>
<group> <group>
<field name="date" readonly="state != 'draft'"/> <field name="date" readonly="state != 'draft'"/>
<field name="picking_id" readonly="1" invisible="not picking_id"/>
</group>
<group>
<field name="note" readonly="state != 'draft'"/> <field name="note" readonly="state != 'draft'"/>
</group> </group>
</group> </group>
<notebook> <notebook>
<page string="GRN Details"> <page string="GRN Details">

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import models

View File

@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
{
'name': 'Grn Purchase',
'version': '1.0',
'summary': 'GRN LINK WITH Purchase',
'description': '''
Detailed description of the module
''',
'category': 'Purchase',
'author': 'Raman Marikanti',
'depends': ['base', 'mail', 'grn','purchase'],
'data': [
# 'security/ir.model.access.csv',
'views/grn_purchase_views.xml',
],
'license': 'LGPL-3',
'installable': True,
'application': False,
'auto_install': False,
}

View File

@ -0,0 +1,3 @@
# -*- coding: utf-8 -*-
from . import grn_purchase

View File

@ -0,0 +1,55 @@
# -*- coding: utf-8 -*-
from odoo import _,api, fields, models
class StockPicking(models.Model):
_inherit = 'stock.picking'
def _action_done(self):
res = super(StockPicking, self)._action_done()
for rec in self:
if rec.purchase_id and rec.picking_type_id.code == 'incoming':
grn = self.env['grn'].search([('picking_id','=', rec.id)])
if not grn:
grn_data = {
'vendor_id':rec.partner_id.id,
'date':fields.Datetime.now(),
'location_id':rec.location_dest_id.id,
'note':rec.purchase_id.name,
'grn_line_ids':[]
}
for line in rec.move_ids.filtered(lambda x:x.product_id.type == 'consu'):
grn_data['grn_line_ids'].append((0,0,{
'product_id':line.product_id.id,
'quantity':line.quantity,
'product_uom_id':line.product_id.uom_id.id,
'price':line.price_unit,
}))
new_grn = self.env['grn'].create(grn_data)
new_grn.name = self.env['ir.sequence'].next_by_code('grn') or _('New')
new_grn.picking_id = rec
new_grn.state = 'done'
rec.purchase_id.grn_ids |= new_grn
elif grn and grn.state != 'done':
grn.state = 'done'
return res
class PurchaseOrder(models.Model):
_inherit = "purchase.order"
grn_ids = fields.Many2many('grn', string="GRN")
grn_count = fields.Integer('GRN Count', compute='_compute_grn_count')
@api.depends('grn_ids')
def _compute_grn_count(self):
self.grn_count = len(self.grn_ids)
def action_show_grn_ids(self):
action = self.env['ir.actions.actions']._for_xml_id('grn.grn_action')
action['domain'] = [('id', 'in', self.grn_ids.ids)]
action['context'] = {}
return action

View File

@ -0,0 +1 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<data>
<record id="purchase_order_form_grn" model="ir.ui.view">
<field name="name">purchase.order.inherited.form.grn</field>
<field name="model">purchase.order</field>
<field name="inherit_id" ref="purchase.purchase_order_form"/>
<field name="arch" type="xml">
<xpath expr="//div[@name='button_box']" position="inside">
<button class="oe_stat_button" name="action_show_grn_ids" type="object" icon="fa-clipboard" invisible="grn_count == 0">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value"><field name="grn_count"/></span>
<span class="o_stat_text">GRN's</span>
</div>
</button>
</xpath>
</field>
</record>
</data>
</odoo>