From f03e6dc2ee2f3cd34eca15e81cd8964c07089ef4 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 12 Sep 2024 10:26:48 +0700 Subject: new window --- indoteknik_custom/models/approval_unreserve.py | 69 ++++++++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 indoteknik_custom/models/approval_unreserve.py (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py new file mode 100644 index 00000000..4feb51fa --- /dev/null +++ b/indoteknik_custom/models/approval_unreserve.py @@ -0,0 +1,69 @@ +from odoo import models, api, fields +from odoo.exceptions import AccessError, UserError, ValidationError +from datetime import timedelta, date +import logging + +_logger = logging.getLogger(__name__) + +class ApprovalUnreserve(models.Model): + _name = "approval.unreserve" + _description = "Approval Unreserve" + _inherit = ['mail.thread'] + _rec_name = 'number' + + number = fields.Char(string='Document No', index=True, copy=False, readonly=True, tracking=True, default='New') + approval_line = fields.One2many('approval.unreserve.line', 'approval_id', string='Approval Unreserve Lines', auto_join=True) + state = fields.Selection([ + ('draft', 'Draft'), + ('waiting_approval', 'Waiting for Approval'), + ('approved', 'Approved'), + ('rejected', 'Rejected') + ], string="Status", default='draft', tracking=True) + request_date = fields.Date(string="Request Date", default=fields.Date.today, tracking=True) + approved_by = fields.Many2one('res.users', string="Approved By", readonly=True, tracking=True) + rejection_reason = fields.Text(string="Rejection Reason", tracking=True) + + @api.model + def create(self, vals): + if vals.get('number', 'New') == 'New': + vals['number'] = self.env['ir.sequence'].next_by_code('approval.unreserve') or 'New' + return super(ApprovalUnreserve, self).create(vals) + + def action_submit_for_approval(self): + self.write({'state': 'waiting_approval'}) + + def action_approve(self): + if self.state != 'waiting_approval': + raise UserError("Approval can only be done in 'Waiting for Approval' state") + self.write({ + 'state': 'approved', + 'approved_by': self.env.user.id + }) + # Trigger the unreserve function + self._trigger_unreserve() + + def action_reject(self, reason): + if self.state != 'waiting_approval': + raise UserError("Rejection can only be done in 'Waiting for Approval' state") + self.write({ + 'state': 'rejected', + 'rejection_reason': reason + }) + + def _trigger_unreserve(self): + # Get the related stock moves and perform the unreserve for approved lines + stock_move_obj = self.env['stock.move'] + for line in self.approval_line: + move = stock_move_obj.browse(line.move_id.id) + # Use the custom _do_unreserve method you defined earlier + move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty) + +class ApprovalUnreserveLine(models.Model): + _name = 'approval.unreserve.line' + _description = 'Approval Unreserve Line' + _order = 'approval_id, id' + + approval_id = fields.Many2one('approval.unreserve', string='Approval Reference', required=True, ondelete='cascade', index=True, copy=False) + move_id = fields.Many2one('stock.move', string="Stock Move", required=True) + product_id = fields.Many2one('product.product', string="Product", related='move_id.product_id', readonly=True) + unreserve_qty = fields.Float(string="Quantity to Unreserve", required=True) -- cgit v1.2.3 From 5c6098158ab0f82437aa24e947a66b78b21b6bd7 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 12 Sep 2024 13:58:27 +0700 Subject: push --- indoteknik_custom/models/approval_unreserve.py | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index 4feb51fa..a8f9fd3b 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -21,8 +21,25 @@ class ApprovalUnreserve(models.Model): ], string="Status", default='draft', tracking=True) request_date = fields.Date(string="Request Date", default=fields.Date.today, tracking=True) approved_by = fields.Many2one('res.users', string="Approved By", readonly=True, tracking=True) + picking_id = fields.Many2one('stock.picking', string="Picking", tracking=True) rejection_reason = fields.Text(string="Rejection Reason", tracking=True) + @api.constrains('picking_id') + def create_move_id_line(self): + if not self.picking_id: + raise ValidationError("Picking is required") + + stock_move = self.env['stock.move'].search([('picking_id', '=', self.picking_id.id)]) + + if not stock_move: + raise ValidationError("Picking is not found") + + for move in stock_move: + self.approval_line.create({ + 'approval_id': self.id, + 'move_id': move.id + }) + @api.model def create(self, vals): if vals.get('number', 'New') == 'New': @@ -66,4 +83,4 @@ class ApprovalUnreserveLine(models.Model): approval_id = fields.Many2one('approval.unreserve', string='Approval Reference', required=True, ondelete='cascade', index=True, copy=False) move_id = fields.Many2one('stock.move', string="Stock Move", required=True) product_id = fields.Many2one('product.product', string="Product", related='move_id.product_id', readonly=True) - unreserve_qty = fields.Float(string="Quantity to Unreserve", required=True) + unreserve_qty = fields.Float(string="Quantity to Unreserve") -- cgit v1.2.3 From b473179e5adde097bc9ec43ee4566b850937f1ed Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Fri, 13 Sep 2024 10:25:03 +0700 Subject: unreserve --- indoteknik_custom/models/approval_unreserve.py | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index a8f9fd3b..8c232d9c 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -22,6 +22,7 @@ class ApprovalUnreserve(models.Model): request_date = fields.Date(string="Request Date", default=fields.Date.today, tracking=True) approved_by = fields.Many2one('res.users', string="Approved By", readonly=True, tracking=True) picking_id = fields.Many2one('stock.picking', string="Picking", tracking=True) + user_id = fields.Many2one('res.users', string="User", readonly=True, tracking=True) rejection_reason = fields.Text(string="Rejection Reason", tracking=True) @api.constrains('picking_id') @@ -40,6 +41,8 @@ class ApprovalUnreserve(models.Model): 'move_id': move.id }) + self.user_id = self.picking_id.sale_id.user_id.id + @api.model def create(self, vals): if vals.get('number', 'New') == 'New': @@ -50,6 +53,9 @@ class ApprovalUnreserve(models.Model): self.write({'state': 'waiting_approval'}) def action_approve(self): + if self.env.user.id != self.user_id.id: + raise UserError("Hanya Sales nya yang bisa approve.") + if self.state != 'waiting_approval': raise UserError("Approval can only be done in 'Waiting for Approval' state") self.write({ @@ -60,6 +66,9 @@ class ApprovalUnreserve(models.Model): self._trigger_unreserve() def action_reject(self, reason): + if self.env.user.id != self.user_id.id: + raise UserError("Hanya Sales nya yang bisa reject.") + if self.state != 'waiting_approval': raise UserError("Rejection can only be done in 'Waiting for Approval' state") self.write({ -- cgit v1.2.3 From 5065ffb7c1dc7113e3d951c0f5a59f2268a88375 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 23 Sep 2024 09:38:08 +0700 Subject: update code --- indoteknik_custom/models/approval_unreserve.py | 27 +++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index 8c232d9c..51bd52d0 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -30,7 +30,7 @@ class ApprovalUnreserve(models.Model): if not self.picking_id: raise ValidationError("Picking is required") - stock_move = self.env['stock.move'].search([('picking_id', '=', self.picking_id.id)]) + stock_move = self.env['stock.move'].search([('picking_id', '=', self.picking_id.id), ('state', '=', 'assigned')]) if not stock_move: raise ValidationError("Picking is not found") @@ -58,6 +58,8 @@ class ApprovalUnreserve(models.Model): if self.state != 'waiting_approval': raise UserError("Approval can only be done in 'Waiting for Approval' state") + + self.write({ 'state': 'approved', 'approved_by': self.env.user.id @@ -77,13 +79,31 @@ class ApprovalUnreserve(models.Model): }) def _trigger_unreserve(self): - # Get the related stock moves and perform the unreserve for approved lines stock_move_obj = self.env['stock.move'] + for line in self.approval_line: move = stock_move_obj.browse(line.move_id.id) - # Use the custom _do_unreserve method you defined earlier + + # Step 1: Unreserve the product from the current picking move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty) + # Step 2: Reserve the product in the destination picking (dest_picking_id) + if line.dest_picking_id: + # Find or create the stock move for the destination picking + dest_move = stock_move_obj.create({ + 'product_id': line.product_id.id, + 'product_uom_qty': line.unreserve_qty, + 'picking_id': line.dest_picking_id.id, + 'name': line.product_id.name, + 'location_id': move.location_dest_id.id, # From the unreserved location + 'location_dest_id': line.dest_picking_id.location_id.id, # To the destination picking's location + 'state': 'draft' # Initial state, to be confirmed later + }) + + # Confirm and reserve the product in the new move + dest_move._action_confirm() # Confirm the move + dest_move._action_assign() # Reserve the product + class ApprovalUnreserveLine(models.Model): _name = 'approval.unreserve.line' _description = 'Approval Unreserve Line' @@ -93,3 +113,4 @@ class ApprovalUnreserveLine(models.Model): move_id = fields.Many2one('stock.move', string="Stock Move", required=True) product_id = fields.Many2one('product.product', string="Product", related='move_id.product_id', readonly=True) unreserve_qty = fields.Float(string="Quantity to Unreserve") + dest_picking_id = fields.Many2one('stock.picking', string="Destination Picking", tracking=True) -- cgit v1.2.3 From 1a50d80c1070f009d93d58f2ff469fbc3daa5623 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 23 Sep 2024 10:27:11 +0700 Subject: push --- indoteknik_custom/models/approval_unreserve.py | 39 +++++++++++++++----------- 1 file changed, 22 insertions(+), 17 deletions(-) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index 51bd52d0..72e3ff51 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -82,27 +82,32 @@ class ApprovalUnreserve(models.Model): stock_move_obj = self.env['stock.move'] for line in self.approval_line: - move = stock_move_obj.browse(line.move_id.id) - # Step 1: Unreserve the product from the current picking + move = stock_move_obj.browse(line.move_id.id) move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty) - # Step 2: Reserve the product in the destination picking (dest_picking_id) + # Step 2: Update existing stock move in dest_picking_id if line.dest_picking_id: - # Find or create the stock move for the destination picking - dest_move = stock_move_obj.create({ - 'product_id': line.product_id.id, - 'product_uom_qty': line.unreserve_qty, - 'picking_id': line.dest_picking_id.id, - 'name': line.product_id.name, - 'location_id': move.location_dest_id.id, # From the unreserved location - 'location_dest_id': line.dest_picking_id.location_id.id, # To the destination picking's location - 'state': 'draft' # Initial state, to be confirmed later - }) - - # Confirm and reserve the product in the new move - dest_move._action_confirm() # Confirm the move - dest_move._action_assign() # Reserve the product + dest_move = stock_move_obj.search([ + ('picking_id', '=', line.dest_picking_id.id), + ('product_id', '=', line.product_id.id), + ('state', 'not in', ['cancel', 'done']) # Only draft or assigned moves can be updated + ], limit=1) + + if dest_move: + # Add the unreserved quantity to the existing stock move + dest_move.write({ + 'product_uom_qty': dest_move.product_uom_qty + line.unreserve_qty + }) + + # If the move is in draft state, confirm it and reserve the product + if dest_move.state == 'draft': + dest_move._action_confirm() + dest_move._action_assign() # Reserve the product + + else: + # If no move is found, raise an error or handle accordingly + raise UserError(f"No stock move found for product {line.product_id.display_name} in the destination picking.") class ApprovalUnreserveLine(models.Model): _name = 'approval.unreserve.line' -- cgit v1.2.3 From b2f9da06ff526a005f7e71c85a4c626971b5c06b Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 23 Sep 2024 11:40:58 +0700 Subject: push --- indoteknik_custom/models/approval_unreserve.py | 42 ++++++++++++++------------ 1 file changed, 22 insertions(+), 20 deletions(-) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index 72e3ff51..00fd1555 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -86,28 +86,30 @@ class ApprovalUnreserve(models.Model): move = stock_move_obj.browse(line.move_id.id) move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty) + line.dest_picking_id.action_assign() + # Step 2: Update existing stock move in dest_picking_id - if line.dest_picking_id: - dest_move = stock_move_obj.search([ - ('picking_id', '=', line.dest_picking_id.id), - ('product_id', '=', line.product_id.id), - ('state', 'not in', ['cancel', 'done']) # Only draft or assigned moves can be updated - ], limit=1) - - if dest_move: - # Add the unreserved quantity to the existing stock move - dest_move.write({ - 'product_uom_qty': dest_move.product_uom_qty + line.unreserve_qty - }) + # if line.dest_picking_id: + # dest_move = stock_move_obj.search([ + # ('picking_id', '=', line.dest_picking_id.id), + # ('product_id', '=', line.product_id.id), + # ('state', 'not in', ['cancel', 'done']) # Only draft or assigned moves can be updated + # ], limit=1) + + # if dest_move: + # # Add the unreserved quantity to the existing stock move + # dest_move.write({ + # 'product_uom_qty': dest_move.product_uom_qty + line.unreserve_qty + # }) - # If the move is in draft state, confirm it and reserve the product - if dest_move.state == 'draft': - dest_move._action_confirm() - dest_move._action_assign() # Reserve the product - - else: - # If no move is found, raise an error or handle accordingly - raise UserError(f"No stock move found for product {line.product_id.display_name} in the destination picking.") + # # If the move is in draft state, confirm it and reserve the product + # if dest_move.state == 'draft': + # dest_move._action_confirm() + # dest_move._action_assign() # Reserve the product + + # else: + # # If no move is found, raise an error or handle accordingly + # raise UserError(f"No stock move found for product {line.product_id.display_name} in the destination picking.") class ApprovalUnreserveLine(models.Model): _name = 'approval.unreserve.line' -- cgit v1.2.3 From be5ae68ee9c9d55611cfbb8243feeee886bc95fd Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 24 Sep 2024 15:47:47 +0700 Subject: push --- indoteknik_custom/models/approval_unreserve.py | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index 00fd1555..e617e11a 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -50,8 +50,28 @@ class ApprovalUnreserve(models.Model): return super(ApprovalUnreserve, self).create(vals) def action_submit_for_approval(self): + self._check_product_and_qty() self.write({'state': 'waiting_approval'}) + + def _check_product_and_qty(self): + stock_move = self.env['stock.move'] + for line in self.approval_line: + if line.dest_picking_id: + move = stock_move.search([ + ('picking_id', '=', line.dest_picking_id.id), + ('product_id', '=', line.product_id.id), + ('state', 'not in', ['done', 'cancel']) + ]) + + if not move: + raise UserError("Product tidak ada di destination picking") + + qty_unreserve = line.unreserve_qty + move.forecast_availability + + if move.product_uom_qty < qty_unreserve: + raise UserError("Quantity yang di unreserve melebihi quantity yang ada") + def action_approve(self): if self.env.user.id != self.user_id.id: raise UserError("Hanya Sales nya yang bisa approve.") @@ -59,7 +79,6 @@ class ApprovalUnreserve(models.Model): if self.state != 'waiting_approval': raise UserError("Approval can only be done in 'Waiting for Approval' state") - self.write({ 'state': 'approved', 'approved_by': self.env.user.id @@ -86,7 +105,8 @@ class ApprovalUnreserve(models.Model): move = stock_move_obj.browse(line.move_id.id) move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty) - line.dest_picking_id.action_assign() + if line.dest_picking_id: + line.dest_picking_id.action_assign() # Step 2: Update existing stock move in dest_picking_id # if line.dest_picking_id: -- cgit v1.2.3 From 5e1a6dc4d2bb04a36fcaef023fb9894336ebd4f6 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 26 Sep 2024 15:05:22 +0700 Subject: cr unreserve --- indoteknik_custom/models/approval_unreserve.py | 53 ++++++++++++++------------ 1 file changed, 28 insertions(+), 25 deletions(-) (limited to 'indoteknik_custom/models/approval_unreserve.py') diff --git a/indoteknik_custom/models/approval_unreserve.py b/indoteknik_custom/models/approval_unreserve.py index e617e11a..88409c37 100644 --- a/indoteknik_custom/models/approval_unreserve.py +++ b/indoteknik_custom/models/approval_unreserve.py @@ -24,6 +24,7 @@ class ApprovalUnreserve(models.Model): picking_id = fields.Many2one('stock.picking', string="Picking", tracking=True) user_id = fields.Many2one('res.users', string="User", readonly=True, tracking=True) rejection_reason = fields.Text(string="Rejection Reason", tracking=True) + reason = fields.Text(string="Reason", tracking=True) @api.constrains('picking_id') def create_move_id_line(self): @@ -99,38 +100,33 @@ class ApprovalUnreserve(models.Model): def _trigger_unreserve(self): stock_move_obj = self.env['stock.move'] - + for line in self.approval_line: - # Step 1: Unreserve the product from the current picking move = stock_move_obj.browse(line.move_id.id) move._do_unreserve(product=line.product_id, quantity=line.unreserve_qty) + original_sale_id = move.picking_id.sale_id + + product_name = line.product_id.display_name + unreserved_qty = line.unreserve_qty + if line.dest_picking_id: + dest_sale_id = line.dest_picking_id.sale_id line.dest_picking_id.action_assign() - # Step 2: Update existing stock move in dest_picking_id - # if line.dest_picking_id: - # dest_move = stock_move_obj.search([ - # ('picking_id', '=', line.dest_picking_id.id), - # ('product_id', '=', line.product_id.id), - # ('state', 'not in', ['cancel', 'done']) # Only draft or assigned moves can be updated - # ], limit=1) - - # if dest_move: - # # Add the unreserved quantity to the existing stock move - # dest_move.write({ - # 'product_uom_qty': dest_move.product_uom_qty + line.unreserve_qty - # }) - - # # If the move is in draft state, confirm it and reserve the product - # if dest_move.state == 'draft': - # dest_move._action_confirm() - # dest_move._action_assign() # Reserve the product - - # else: - # # If no move is found, raise an error or handle accordingly - # raise UserError(f"No stock move found for product {line.product_id.display_name} in the destination picking.") - + if original_sale_id: + message = ( + f"Barang {product_name} sebanyak {unreserved_qty} dipindahkan ke SO {dest_sale_id.name}" + if dest_sale_id else + f"Barang {product_name} sebanyak {unreserved_qty} dipindahkan ke picking tujuan." + ) + original_sale_id.message_post(body=message) + else: + if original_sale_id: + message = f"Barang {product_name} sebanyak {unreserved_qty} ter unreserve." + original_sale_id.message_post(body=message) + + class ApprovalUnreserveLine(models.Model): _name = 'approval.unreserve.line' _description = 'Approval Unreserve Line' @@ -139,5 +135,12 @@ class ApprovalUnreserveLine(models.Model): approval_id = fields.Many2one('approval.unreserve', string='Approval Reference', required=True, ondelete='cascade', index=True, copy=False) move_id = fields.Many2one('stock.move', string="Stock Move", required=True) product_id = fields.Many2one('product.product', string="Product", related='move_id.product_id', readonly=True) + sales_id = fields.Many2one('res.users', string="Sales", readonly=True, tracking=True) unreserve_qty = fields.Float(string="Quantity to Unreserve") dest_picking_id = fields.Many2one('stock.picking', string="Destination Picking", tracking=True) + + + @api.onchange('dest_picking_id') + def onchange_dest_picking_id(self): + if self.dest_picking_id: + self.sales_id = self.dest_picking_id.sale_id.user_id.id -- cgit v1.2.3