From 9ee856d603530e8cc3494a2bccb8fdfaa328da6a Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 3 Sep 2024 14:14:09 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 9 +++++++++ indoteknik_custom/models/stock_picking.py | 19 +++++++++++++++++-- 2 files changed, 26 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 710e99de..2769d408 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -109,6 +109,15 @@ class SaleOrder(models.Model): date_driver_departure = fields.Datetime(string='Departure Date', compute='_compute_date_kirim', copy=False) note_website = fields.Char(string="Note Website") use_button = fields.Boolean(string='Using Calculate Selling Price', copy=False) + unreserve_id = fields.Many2one('stock.picking', 'Unreserve Picking') + + def do_unreserve(self): + user_id = self.env.user.id + if user_id != self.user_id.id: + raise UserError(_("Only the user who created the picking can unreserve it.")) + + self.unreserve_id.do_unreserve() + def _compute_date_kirim(self): for rec in self: diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index d16d508e..474c7526 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -125,12 +125,27 @@ class StockPicking(models.Model): raise UserError('Hanya Logistic yang bisa mengubah shipping method') def do_unreserve(self): + if self.sale_id.unreserve_id.id != self.id: + self.sale_id.unreserve_id = self.id + return self._create_approval_notification('Logistic') + res = super(StockPicking, self).do_unreserve() - if not self.env.user.is_purchasing_manager: - raise UserError('Hanya Purchasing Manager yang bisa Unreserve') current_time = datetime.datetime.utcnow() self.date_unreserve = current_time + return res + + def _create_approval_notification(self, approval_role): + title = 'Warning' + message = f'Butuh approval sales untuk unreserved' + return self._create_notification_action(title, message) + + def _create_notification_action(self, title, message): + return { + 'type': 'ir.actions.client', + 'tag': 'display_notification', + 'params': { 'title': title, 'message': message, 'next': {'type': 'ir.actions.act_window_close'} }, + } def _compute_shipping_status(self): for rec in self: -- cgit v1.2.3 From 8edde46e16d4d977e84a3681c03d1bfab8591a71 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Tue, 3 Sep 2024 15:20:06 +0700 Subject: cr unreserved --- indoteknik_custom/models/sale_order.py | 3 +-- indoteknik_custom/models/stock_picking.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 2769d408..d389f6ae 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -116,9 +116,8 @@ class SaleOrder(models.Model): if user_id != self.user_id.id: raise UserError(_("Only the user who created the picking can unreserve it.")) - self.unreserve_id.do_unreserve() + self.unreserve_id.with_context({'darimana': 'sale.order'}).do_unreserve() - def _compute_date_kirim(self): for rec in self: picking = self.env['stock.picking'].search([('sale_id', '=', rec.id), ('state', 'not in', ['cancel'])], order='date_doc_kirim desc', limit=1) diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py index 474c7526..de45af2e 100644 --- a/indoteknik_custom/models/stock_picking.py +++ b/indoteknik_custom/models/stock_picking.py @@ -125,7 +125,7 @@ class StockPicking(models.Model): raise UserError('Hanya Logistic yang bisa mengubah shipping method') def do_unreserve(self): - if self.sale_id.unreserve_id.id != self.id: + if not self._context.get('darimana') == 'sale.order': self.sale_id.unreserve_id = self.id return self._create_approval_notification('Logistic') -- cgit v1.2.3 From 82f0467cd3f014761fd35bd04eecc485ed8addda Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Thu, 12 Sep 2024 09:46:14 +0700 Subject: push --- indoteknik_custom/models/sale_order.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index d389f6ae..abfbb799 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -113,10 +113,13 @@ class SaleOrder(models.Model): def do_unreserve(self): user_id = self.env.user.id - if user_id != self.user_id.id: + if user_id != self.user_id.id and user_id != self.helper_by_id.id: raise UserError(_("Only the user who created the picking can unreserve it.")) - self.unreserve_id.with_context({'darimana': 'sale.order'}).do_unreserve() + if self.unreserve_id and self.unreserve_id.state == 'assigned': + self.unreserve_id.with_context({'darimana': 'sale.order'}).do_unreserve() + else: + raise UserError(_("Picking not found.")) def _compute_date_kirim(self): for rec in self: -- cgit v1.2.3 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/__init__.py | 1 + indoteknik_custom/models/approval_unreserve.py | 69 ++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 indoteknik_custom/models/approval_unreserve.py (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py index e9ce587c..fe3e02d4 100755 --- a/indoteknik_custom/models/__init__.py +++ b/indoteknik_custom/models/__init__.py @@ -125,3 +125,4 @@ from . import sale_order_multi_uangmuka_penjualan from . import shipment_group from . import sales_order_reject from . import approval_date_doc +from . import approval_unreserve 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 ++++++++++- indoteknik_custom/models/stock_move.py | 44 +++++++++++++++++++++++++- 2 files changed, 61 insertions(+), 2 deletions(-) (limited to 'indoteknik_custom/models') 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") diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index fe46bf65..6c44f8a6 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -1,5 +1,6 @@ from odoo import fields, models, api - +from odoo.tools.misc import format_date, OrderedSet +from odoo.exceptions import UserError class StockMove(models.Model): _inherit = 'stock.move' @@ -7,6 +8,47 @@ class StockMove(models.Model): line_no = fields.Integer('No', default=0) sale_id = fields.Many2one('sale.order', string='SO') + def _do_unreserve(self, product=None, quantity=None): + moves_to_unreserve = OrderedSet() + for move in self: + if move.state == 'cancel' or (move.state == 'done' and move.scrapped): + continue + elif move.state == 'done': + raise UserError(_("You cannot unreserve a stock move that has been set to 'Done'.")) + + if product and move.product_id != product: + continue # Skip moves that don't match the specified product + moves_to_unreserve.add(move.id) + + moves_to_unreserve = self.env['stock.move'].browse(moves_to_unreserve) + + ml_to_update, ml_to_unlink = OrderedSet(), OrderedSet() + moves_not_to_recompute = OrderedSet() + + for ml in moves_to_unreserve.move_line_ids: + if product and ml.product_id != product: + continue # Only affect the specified product + + if quantity > 0: + # Only reduce by the specified quantity if it is greater than zero + ml_to_update.add(ml.id) + remaining_qty = ml.product_uom_qty - quantity + ml.write({'product_uom_qty': remaining_qty if remaining_qty > 0 else 0}) + quantity = 0 # Set to zero to prevent further unreserving in the same loop + elif ml.qty_done: + ml_to_update.add(ml.id) + else: + ml_to_unlink.add(ml.id) + moves_not_to_recompute.add(ml.move_id.id) + + ml_to_update, ml_to_unlink = self.env['stock.move.line'].browse(ml_to_update), self.env['stock.move.line'].browse(ml_to_unlink) + moves_not_to_recompute = self.env['stock.move'].browse(moves_not_to_recompute) + + ml_to_unlink.unlink() + (moves_to_unreserve - moves_not_to_recompute)._recompute_state() + return True + + def _prepare_account_move_line_from_mr(self, po_line, qty, move=False): po_line.ensure_one() aml_currency = move and move.currency_id or po_line.currency_id -- 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 +++++++++ indoteknik_custom/models/sale_order.py | 10 ---------- indoteknik_custom/models/sale_order_line.py | 2 +- indoteknik_custom/models/stock_move.py | 2 +- 4 files changed, 11 insertions(+), 12 deletions(-) (limited to 'indoteknik_custom/models') 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({ diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 023cc5e6..0690ffa2 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -113,16 +113,6 @@ class SaleOrder(models.Model): use_button = fields.Boolean(string='Using Calculate Selling Price', copy=False) unreserve_id = fields.Many2one('stock.picking', 'Unreserve Picking') voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Voucher Shipping', copy=False) - - def do_unreserve(self): - user_id = self.env.user.id - if user_id != self.user_id.id and user_id != self.helper_by_id.id: - raise UserError(_("Only the user who created the picking can unreserve it.")) - - if self.unreserve_id and self.unreserve_id.state == 'assigned': - self.unreserve_id.with_context({'darimana': 'sale.order'}).do_unreserve() - else: - raise UserError(_("Picking not found.")) def _compute_date_kirim(self): for rec in self: diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index a64a744c..50438dbe 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -348,7 +348,7 @@ class SaleOrderLine(models.Model): def validate_line(self): for line in self: - if line.product_id.id in [385544, 224484]: + if line.product_id.id in [385544, 224484, 417724]: raise UserError('Produk Sementara Tidak Bisa Di Confirm atau Ask Approval') if not line.product_id or line.product_id.type == 'service': continue diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 6c44f8a6..cd061946 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -14,7 +14,7 @@ class StockMove(models.Model): if move.state == 'cancel' or (move.state == 'done' and move.scrapped): continue elif move.state == 'done': - raise UserError(_("You cannot unreserve a stock move that has been set to 'Done'.")) + raise UserError("You cannot unreserve a stock move that has been set to 'Done'.") if product and move.product_id != product: continue # Skip moves that don't match the specified product -- 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') 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') 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 ++++++++++++++------------ indoteknik_custom/models/stock_move.py | 9 +++--- 2 files changed, 27 insertions(+), 24 deletions(-) (limited to 'indoteknik_custom/models') 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' diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index cd061946..32ef312f 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -19,17 +19,17 @@ class StockMove(models.Model): if product and move.product_id != product: continue # Skip moves that don't match the specified product moves_to_unreserve.add(move.id) - + moves_to_unreserve = self.env['stock.move'].browse(moves_to_unreserve) ml_to_update, ml_to_unlink = OrderedSet(), OrderedSet() moves_not_to_recompute = OrderedSet() - + for ml in moves_to_unreserve.move_line_ids: if product and ml.product_id != product: continue # Only affect the specified product - if quantity > 0: + if quantity is not None and quantity > 0: # Only reduce by the specified quantity if it is greater than zero ml_to_update.add(ml.id) remaining_qty = ml.product_uom_qty - quantity @@ -40,7 +40,7 @@ class StockMove(models.Model): else: ml_to_unlink.add(ml.id) moves_not_to_recompute.add(ml.move_id.id) - + ml_to_update, ml_to_unlink = self.env['stock.move.line'].browse(ml_to_update), self.env['stock.move.line'].browse(ml_to_unlink) moves_not_to_recompute = self.env['stock.move'].browse(moves_not_to_recompute) @@ -49,6 +49,7 @@ class StockMove(models.Model): return True + def _prepare_account_move_line_from_mr(self, po_line, qty, move=False): po_line.ensure_one() aml_currency = move and move.currency_id or po_line.currency_id -- cgit v1.2.3 From 1f9d0136758531831ea6c7a90556c9a472ed8d40 Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 23 Sep 2024 13:20:39 +0700 Subject: push --- indoteknik_custom/models/stock_move.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py index 32ef312f..ac2e3cc0 100644 --- a/indoteknik_custom/models/stock_move.py +++ b/indoteknik_custom/models/stock_move.py @@ -8,7 +8,7 @@ class StockMove(models.Model): line_no = fields.Integer('No', default=0) sale_id = fields.Many2one('sale.order', string='SO') - def _do_unreserve(self, product=None, quantity=None): + def _do_unreserve(self, product=None, quantity=False): moves_to_unreserve = OrderedSet() for move in self: if move.state == 'cancel' or (move.state == 'done' and move.scrapped): @@ -19,17 +19,17 @@ class StockMove(models.Model): if product and move.product_id != product: continue # Skip moves that don't match the specified product moves_to_unreserve.add(move.id) - + moves_to_unreserve = self.env['stock.move'].browse(moves_to_unreserve) ml_to_update, ml_to_unlink = OrderedSet(), OrderedSet() moves_not_to_recompute = OrderedSet() - + for ml in moves_to_unreserve.move_line_ids: if product and ml.product_id != product: continue # Only affect the specified product - if quantity is not None and quantity > 0: + if quantity and quantity > 0: # Only reduce by the specified quantity if it is greater than zero ml_to_update.add(ml.id) remaining_qty = ml.product_uom_qty - quantity @@ -40,7 +40,7 @@ class StockMove(models.Model): else: ml_to_unlink.add(ml.id) moves_not_to_recompute.add(ml.move_id.id) - + ml_to_update, ml_to_unlink = self.env['stock.move.line'].browse(ml_to_update), self.env['stock.move.line'].browse(ml_to_unlink) moves_not_to_recompute = self.env['stock.move'].browse(moves_not_to_recompute) @@ -49,7 +49,6 @@ class StockMove(models.Model): return True - def _prepare_account_move_line_from_mr(self, po_line, qty, move=False): po_line.ensure_one() aml_currency = move and move.currency_id or po_line.currency_id -- 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') 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') 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 From e48193b793216a4ab82d88753ae144c08c3ddfaf Mon Sep 17 00:00:00 2001 From: Azka Nathan Date: Mon, 30 Sep 2024 14:04:23 +0700 Subject: estimated shipping price --- indoteknik_custom/models/sale_order.py | 48 +++++++++++++++++++++++++++++ indoteknik_custom/models/sale_order_line.py | 2 ++ 2 files changed, 50 insertions(+) (limited to 'indoteknik_custom/models') diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py index 3a28bc54..798b9109 100755 --- a/indoteknik_custom/models/sale_order.py +++ b/indoteknik_custom/models/sale_order.py @@ -131,6 +131,54 @@ class SaleOrder(models.Model): payment_term_id = fields.Many2one( 'account.payment.term', string='Payment Terms', check_company=True, # Unrequired company domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]", tracking=True) + + def action_estimate_shipping(self): + total_weight = 0 + missing_weight_products = [] + + # Menghitung total berat dari Sale Order Line + for line in self.order_line: + if line.weight: + total_weight += line.weight * line.product_uom_qty + line.product_id.weight = line.weight + else: + missing_weight_products.append(line.product_id.name) + + # Menampilkan pesan jika ada produk tanpa berat + if missing_weight_products: + self.message_post(body="Warning: Beberapa produk tidak memiliki berat: %s" % ', '.join(missing_weight_products)) + + if total_weight == 0: + raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.") + + # Panggil API Raja Ongkir untuk mendapatkan estimasi ongkir + result = self._call_rajaongkir_api(total_weight) + if result: + estimated_cost = result['rajaongkir']['results'][0]['costs'][0]['cost'][0]['value'] + # Memasukkan hasil estimasi ke field delivery_amt + self.delivery_amt = estimated_cost + self.message_post(body=f"Estimasi Ongkos Kirim: {estimated_cost}") + else: + raise UserError("Gagal mendapatkan estimasi ongkir.") + + def _call_rajaongkir_api(self, total_weight): + url = 'https://pro.rajaongkir.com/api/cost' + headers = { + 'key': '7ac9883688da043b50cc32f0e3070bb6', + } + courier = self.carrier_id.name.lower() + origin = self.partner_shipping_id.city_id.state_id.code + destination = self.partner_shipping_id.city_id.state_id.code + data = { + 'origin': origin, # Contoh ID kota asal (Yogyakarta) + 'destination': destination, # Contoh ID kota tujuan (Jakarta) + 'weight': int(total_weight * 1000), # Menggunakan berat dalam gram + 'courier': courier, + } + response = requests.post(url, headers=headers, data=data) + if response.status_code == 200: + return response.json() + return None def _compute_type_promotion(self): for rec in self: diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py index 50438dbe..0ea6a2cc 100644 --- a/indoteknik_custom/models/sale_order_line.py +++ b/indoteknik_custom/models/sale_order_line.py @@ -31,6 +31,7 @@ class SaleOrderLine(models.Model): qty_reserved = fields.Float(string='Qty Reserved', compute='_compute_qty_reserved') reserved_from = fields.Char(string='Reserved From', copy=False) item_percent_margin_without_deduction = fields.Float('%Margin', compute='_compute_item_margin_without_deduction') + weight = fields.Float(string='Weight') @api.constrains('note_procurement') def note_procurement_to_apo(self): @@ -244,6 +245,7 @@ class SaleOrderLine(models.Model): # query, limit=1, order='count_trx_po desc, count_trx_po_vendor desc') price, taxes, vendor_id = self._get_purchase_price(line.product_id) line.vendor_id = vendor_id + line.weight = line.product_id.weight line.tax_id = line.order_id.sales_tax_id # price, taxes = line._get_valid_purchase_price(purchase_price) line.purchase_price = price -- cgit v1.2.3