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) 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): if not self.picking_id: raise ValidationError("Picking is required") stock_move = self.env['stock.move'].search([('picking_id', '=', self.picking_id.id), ('state', 'in', ['assigned', 'partially_available'])]) 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 }) self.user_id = self.picking_id.sale_id.user_id.id @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._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.reserved_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 and not self.env.user.has_group('indoteknik_custom.group_role_it'): 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({ 'state': 'approved', 'approved_by': self.env.user.id }) # Trigger the unreserve function self._trigger_unreserve() # self.picking_id.check_state_reserve() 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({ 'state': 'rejected', 'rejection_reason': reason }) def _trigger_unreserve(self): stock_move_obj = self.env['stock.move'] for line in self.approval_line: 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() 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' _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) 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