summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAzka Nathan <darizkyfaz@gmail.com>2024-09-30 14:08:26 +0700
committerAzka Nathan <darizkyfaz@gmail.com>2024-09-30 14:08:26 +0700
commit3b83868fbdb29e8f5208035e5166a13e5d24f382 (patch)
tree437eae685994f9f85682fe53805a476b45164d6d
parente48193b793216a4ab82d88753ae144c08c3ddfaf (diff)
parent5e1a6dc4d2bb04a36fcaef023fb9894336ebd4f6 (diff)
Merge branch 'unreserved_permission' into production
# Conflicts: # indoteknik_custom/models/__init__.py # indoteknik_custom/security/ir.model.access.csv
-rwxr-xr-xindoteknik_custom/__manifest__.py1
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/approval_unreserve.py146
-rwxr-xr-xindoteknik_custom/models/sale_order.py1
-rw-r--r--indoteknik_custom/models/stock_move.py44
-rw-r--r--indoteknik_custom/models/stock_picking.py19
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv2
-rw-r--r--indoteknik_custom/views/approval_unreserve.xml81
-rw-r--r--indoteknik_custom/views/ir_sequence.xml10
-rwxr-xr-xindoteknik_custom/views/sale_order.xml1
10 files changed, 303 insertions, 3 deletions
diff --git a/indoteknik_custom/__manifest__.py b/indoteknik_custom/__manifest__.py
index 8d8e8cec..e1a67592 100755
--- a/indoteknik_custom/__manifest__.py
+++ b/indoteknik_custom/__manifest__.py
@@ -142,6 +142,7 @@
'views/approval_date_doc.xml',
'views/partner_payment_term.xml',
'views/vendor_payment_term.xml',
+ 'views/approval_unreserve.xml',
'report/report.xml',
'report/report_banner_banner.xml',
'report/report_banner_banner2.xml',
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index 4619147a..3d700ce0 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -127,3 +127,4 @@ from . import shipment_group
from . import sales_order_reject
from . import approval_date_doc
from . import account_tax
+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..88409c37
--- /dev/null
+++ b/indoteknik_custom/models/approval_unreserve.py
@@ -0,0 +1,146 @@
+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', '=', 'assigned')])
+
+ 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.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.")
+
+ 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.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
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 798b9109..d0a34007 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -111,6 +111,7 @@ 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')
voucher_shipping_id = fields.Many2one(comodel_name='voucher', string='Voucher Shipping', copy=False)
margin_after_delivery_purchase = fields.Float(string='Margin After Delivery Purchase', compute='_compute_margin_after_delivery_purchase')
percent_margin_after_delivery_purchase = fields.Float(string='% Margin After Delivery Purchase', compute='_compute_margin_after_delivery_purchase')
diff --git a/indoteknik_custom/models/stock_move.py b/indoteknik_custom/models/stock_move.py
index fe46bf65..ac2e3cc0 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=False):
+ 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 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
+ 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
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 3dd960e2..66a326ff 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 not self._context.get('darimana') == 'sale.order':
+ 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:
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 61ced26c..19e3bdca 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -136,3 +136,5 @@ access_shipment_group_line,access.shipment.group.line,model_shipment_group_line,
access_sales_order_reject,access.sales.order.reject,model_sales_order_reject,,1,1,1,1
access_approval_date_doc,access.approval.date.doc,model_approval_date_doc,,1,1,1,1
access_account_tax,access.account.tax,model_account_tax,,1,1,1,1
+access_approval_unreserve,access.approval.unreserve,model_approval_unreserve,,1,1,1,1
+access_approval_unreserve_line,access.approval.unreserve.line,model_approval_unreserve_line,,1,1,1,1
diff --git a/indoteknik_custom/views/approval_unreserve.xml b/indoteknik_custom/views/approval_unreserve.xml
new file mode 100644
index 00000000..317b5276
--- /dev/null
+++ b/indoteknik_custom/views/approval_unreserve.xml
@@ -0,0 +1,81 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<odoo>
+ <record id="view_approval_unreserve_tree" model="ir.ui.view">
+ <field name="name">approval.unreserve.tree</field>
+ <field name="model">approval.unreserve</field>
+ <field name="arch" type="xml">
+ <tree string="Approval Unreserve">
+ <field name="number"/>
+ <field name="request_date"/>
+ <field name="picking_id"/>
+ <field name="state"/>
+ <field name="user_id"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="approval_unreserve_line_tree" model="ir.ui.view">
+ <field name="name">approval.unreserve.line.tree</field>
+ <field name="model">approval.unreserve.line</field>
+ <field name="arch" type="xml">
+ <tree editable="bottom">
+ <field name="move_id"/>
+ <field name="product_id"/>
+ <field name="dest_picking_id"/>
+ <field name="sales_id"/>
+ <field name="unreserve_qty"/>
+ </tree>
+ </field>
+ </record>
+
+ <record id="approval_unreserve_form" model="ir.ui.view">
+ <field name="name">approval.unreserve.form</field>
+ <field name="model">approval.unreserve</field>
+ <field name="arch" type="xml">
+ <form create="false">
+ <header>
+ <button name="action_submit_for_approval" type="object" string="Submit for Approval" attrs="{'invisible': [('state', '!=', 'draft')]}"/>
+ <button name="action_approve" type="object" string="Approve" attrs="{'invisible': [('state', '!=', 'waiting_approval')]}"/>
+ <button name="action_reject" type="object" string="Reject" attrs="{'invisible': [('state', '!=', 'waiting_approval')]}"/>
+ </header>
+ <sheet>
+ <group>
+ <group>
+ <field name="number" readonly="1"/>
+ <field name="request_date" readonly="1"/>
+ <field name="picking_id"/>
+ <field name="user_id" readonly="1"/>
+ <field name="state" readonly="1"/>
+ <field name="approved_by" readonly="1"/>
+ <field name="reason"/>
+ </group>
+ </group>
+ <notebook>
+ <page string="Lines">
+ <field name="approval_line"/>
+ </page>
+ </notebook>
+ </sheet>
+ <div class="oe_chatter">
+ <field name="message_follower_ids" widget="mail_followers"/>
+ <field name="message_ids" widget="mail_thread"/>
+ </div>
+ </form>
+ </field>
+ </record>
+
+ <record id="approval_unreserve_action" model="ir.actions.act_window">
+ <field name="name">Approval Unreserve</field>
+ <field name="type">ir.actions.act_window</field>
+ <field name="res_model">approval.unreserve</field>
+ <field name="view_mode">tree,form</field>
+ </record>
+
+ <menuitem
+ id="menu_approval_unreserve"
+ name="Approval Unreserve"
+ parent="sale.product_menu_catalog"
+ sequence="4"
+ action="approval_unreserve_action"
+ />
+</odoo>
diff --git a/indoteknik_custom/views/ir_sequence.xml b/indoteknik_custom/views/ir_sequence.xml
index b2768c71..dd501d8c 100644
--- a/indoteknik_custom/views/ir_sequence.xml
+++ b/indoteknik_custom/views/ir_sequence.xml
@@ -20,6 +20,16 @@
<field name="number_next">1</field>
<field name="number_increment">1</field>
</record>
+
+ <record id="sequence_approval_unreserve" model="ir.sequence">
+ <field name="name">Approval Unreserve</field>
+ <field name="code">approval.unreserve</field>
+ <field name="active">TRUE</field>
+ <field name="prefix">AU/%(year)s/</field>
+ <field name="padding">5</field>
+ <field name="number_next">1</field>
+ <field name="number_increment">1</field>
+ </record>
<record id="sequence_logbook_sj" model="ir.sequence">
<field name="name">Logbook SJ</field>
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index 5ef399c7..17faaa95 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -78,6 +78,7 @@
<field name="npwp" placeholder='99.999.999.9-999.999' required="1"/>
<field name="sppkp" attrs="{'required': [('customer_type', '=', 'pkp')]}"/>
<field name="email" required="1"/>
+ <field name="unreserve_id"/>
<field name="due_id" readonly="1"/>
<field name="source_id" domain="[('id', 'in', [32, 59, 60, 61])]" required="1"/>
<button name="override_allow_create_invoice"