odoo18/custom_addons/grn/models/grn.py

220 lines
8.1 KiB
Python

# -*- coding: utf-8 -*-
from email.policy import default
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError, UserError
class Grn(models.Model):
_name = 'grn'
_description = 'Goods Receipt Note'
_inherit = ['mail.thread', 'mail.activity.mixin', 'product.catalog.mixin']
name = fields.Char(
string='Name',
copy=False,
readonly=True,
default=lambda self: _('New')
)
vendor_id = fields.Many2one('res.partner', string='Vendor', readonly=True, )
note = fields.Text(string='Source Document / Note')
date = fields.Date(string='Date', default=fields.Date.context_today, copy=False)
picking_id = fields.Many2one("stock.picking", string="Stock Transfer", copy=False)
state = fields.Selection(
[('draft', 'Draft'), ('confirmed', 'Confirmed'), ('done', 'Done'), ('cancel', 'Cancelled')],
string='State',
default='draft',
tracking=True
)
grn_line_ids = fields.One2many('grn.lines', 'grn_id', string="GRN Lines")
received_by = fields.Many2one('res.users', string='Received By')
location_id = fields.Many2one(
'stock.location',
string='To',
domain="[('usage', '=', 'internal')]",
required=True
)
total_amount = fields.Float(string='Amount', compute='_compute_total_amount')
company_id = fields.Many2one(
'res.company',
string='Company',
required=True,
readonly=True,
default=lambda self: self.env.company,
)
def _get_product_catalog_order_data(self, products, **kwargs):
res = super()._get_product_catalog_order_data(products, **kwargs)
return res
@api.depends('grn_line_ids')
def _compute_total_amount(self):
"""Compute the value of the field computed_field."""
for record in self:
amount = 0
for line in record.grn_line_ids:
amount += (line.quantity * line.price)
record.total_amount = amount
def button_action_confirm(self):
"""Confirm the GRN and assign sequence and received user"""
if not self.grn_line_ids:
raise UserError(_(
"Cannot confirm the GRN '%s' because no products have been added in the lines. "
"Please add at least one product before confirming."
) % self.name)
if not self.name or self.name == _('New'):
self.name = self.env['ir.sequence'].next_by_code('grn') or _('New')
self.state = 'confirmed'
self.received_by = self.env.user
for line in self.grn_line_ids:
if line.price:
continue
else:
line.price = line.product_id.standard_price
def button_action_done(self):
"""Set GRN state to done if picking is completed"""
if not self.picking_id or self.picking_id.state != 'done':
raise ValidationError(_(
"Cannot mark the GRN '%s' as done because the associated stock transfer is not yet completed. "
"Please complete the transfer before proceeding."
) % (self.name,))
if self.state == 'confirmed':
self.state = 'done'
def button_action_cancel(self):
"""Cancel GRN if stock transfer is not done"""
if self.picking_id and self.picking_id.state == 'done':
raise ValidationError(_(
"Cannot cancel the GRN '%s' because the stock transfer '%s' has already been completed. "
) % (self.name, self.picking_id.name))
if self.picking_id:
self.picking_id.action_cancel()
self.picking_id.unlink()
self.state = 'cancel'
def button_action_reset_to_draft(self):
self.state = 'draft'
def button_create_transfer(self):
"""Create stock picking for confirmed GRN"""
if self.picking_id:
return
if self.state != 'confirmed':
raise UserError(_(
"The GRN '%s' cannot be transferred because it is not confirmed yet. "
"Please confirm the GRN first."
) % self.name)
picking_type = self.env['stock.picking.type'].search([('code', '=', 'incoming')], limit=1)
if not picking_type:
raise UserError(_(
"No Incoming Picking Type is configured in the system. "
"Please create an Incoming Picking Type before transferring GRN '%s'."
) % self.name)
picking = self.env['stock.picking'].create({
'origin': self.name,
'location_dest_id': self.location_id.id,
'location_id': picking_type.default_location_src_id.id,
'picking_type_id': picking_type.id,
'partner_id':self.vendor_id.id
})
moves = []
for line in self.grn_line_ids:
moves.append((0, 0, {
'name': line.product_id.name,
'product_id': line.product_id.id,
'product_uom': line.product_uom_id.id,
'product_uom_qty': line.quantity,
'location_id': picking_type.default_location_src_id.id,
'location_dest_id': self.location_id.id,
'price_unit': line.price if line.price > 1 else line.product_id.standard_price,
}))
picking.write({'move_ids_without_package': moves})
picking.action_confirm()
for move in picking:
move.location_dest_id = self.location_id
picking.location_dest_id = self.location_id
self.picking_id = picking
def action_view_transfer(self):
"""Return action to view related stock picking"""
self.ensure_one()
return {
'type': 'ir.actions.act_window',
'name': _('Receipts'),
'res_model': 'stock.picking',
'view_mode': 'form',
'target': 'current',
'res_id': self.picking_id.id,
}
@api.model_create_multi
def create(self, vals_list):
return super().create(vals_list)
def write(self, vals):
return super().write(vals)
def unlink(self):
if any(rec.state not in ['cancel', 'draft'] for rec in self):
raise UserError(_(
"Unable to delete the GRN '%s' because it has already been confirmed or processed. "
) % self.name)
return super().unlink()
def action_add_from_catalog(self):
res = super().action_add_from_catalog()
return res
class GrnLines(models.Model):
_name = 'grn.lines'
_description = 'GRN Lines'
grn_id = fields.Many2one('grn', string="GRN", required=True, ondelete='cascade')
product_id = fields.Many2one(
'product.product',
string='Product',
ondelete="cascade",
domain=[('type', '!=', 'service'),('purchase_ok','=',True)],
index=True
)
product_uom_id = fields.Many2one(related='product_id.uom_id', string='Unit of Measure')
quantity = fields.Float('Quantity', digits='Product Unit of Measure')
price = fields.Float("Unit Price")
total_price = fields.Float("Total Price", compute='_compute_product_total_price', store=True)
@api.depends('quantity', 'price')
def _compute_product_total_price(self):
for rec in self:
rec.total_price = rec.quantity * rec.price if rec.quantity and rec.price else 0
@api.constrains('product_id')
def _check_unique_product_grn(self):
for rec in self:
duplicate_lines = rec.grn_id.grn_line_ids.filtered(lambda l: l.product_id == rec.product_id)
if len(duplicate_lines) > 1:
raise UserError(_('The product %s already exists in the GRN.') % rec.product_id.display_name)
def action_add_from_catalog(self):
order = self.env['grn'].browse(self.env.context.get('order_id'))
return order.with_context(child_field='grn_line_ids').action_add_from_catalog()
def unlink(self):
if any(rec.grn_id.state not in ['cancel', 'draft'] for rec in self):
raise UserError(_(
"Unable to delete the GRN '%s' because it has already been confirmed or processed. "
) % self.name)
return super().unlink()