summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models
diff options
context:
space:
mode:
authorMqdd <ahmadmiqdad27@gmail.com>2026-02-14 11:14:01 +0700
committerMqdd <ahmadmiqdad27@gmail.com>2026-02-14 11:14:01 +0700
commit472f36669375d3d0bcecf137e07260fac25d12b3 (patch)
tree873a665fd8d3cc249a6460858e85272b838162d3 /indoteknik_custom/models
parent24b33148858c9827ad0b14e716562c377e1644ca (diff)
parent37e0beac646ee2e676ff935e8289cf3189b3c21b (diff)
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into gudang-service
Diffstat (limited to 'indoteknik_custom/models')
-rwxr-xr-xindoteknik_custom/models/__init__.py4
-rw-r--r--indoteknik_custom/models/account_move.py16
-rw-r--r--indoteknik_custom/models/account_move_line.py65
-rw-r--r--indoteknik_custom/models/advance_payment_request.py14
-rwxr-xr-xindoteknik_custom/models/purchase_order.py29
-rwxr-xr-xindoteknik_custom/models/purchase_order_line.py46
-rw-r--r--indoteknik_custom/models/refund_sale_order.py57
-rwxr-xr-xindoteknik_custom/models/sale_order.py18
-rw-r--r--indoteknik_custom/models/sale_order_line.py53
-rw-r--r--indoteknik_custom/models/shipment_group.py24
-rw-r--r--indoteknik_custom/models/stock_inventory.py26
-rw-r--r--indoteknik_custom/models/stock_picking.py13
-rw-r--r--indoteknik_custom/models/tukar_guling_po.py38
-rw-r--r--indoteknik_custom/models/update_depreciation_move_wizard.py48
-rwxr-xr-xindoteknik_custom/models/x_manufactures.py2
15 files changed, 413 insertions, 40 deletions
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index e6a59246..2b1851ce 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -165,4 +165,8 @@ from . import partial_delivery
from . import domain_apo
from . import uom_uom
from . import commission_internal
+<<<<<<< HEAD
from . import gudang_service
+=======
+from . import update_depreciation_move_wizard
+>>>>>>> 37e0beac646ee2e676ff935e8289cf3189b3c21b
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index e1360cfa..42467b78 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -587,6 +587,10 @@ class AccountMove(models.Model):
records = self.search([('id', 'in', self.ids)])
template = self.env.ref('indoteknik_custom.mail_template_efaktur_document')
+ ICP = self.env['ir.config_parameter'].sudo()
+ special_partner_ids = set(
+ int(x) for x in (ICP.get_param('efaktur.special_partner_ids') or '').split(',') if x
+ )
for record in records:
if record.invoice_payment_term_id.id == 26:
@@ -595,6 +599,18 @@ class AccountMove(models.Model):
'attachment_ids': [(4, attachment.id)]
}
template.send_mail(record.id, email_values=email_values, force_send=True)
+
+ elif record.partner_id.id in special_partner_ids:
+ attachment = self.generate_attachment(record)
+ email_list = [record.partner_id.email] if record.partner_id.email else []
+ if record.real_invoice_id and record.real_invoice_id.email:
+ email_list.append(record.real_invoice_id.email)
+
+ email_values = {
+ 'email_to': ",".join(set(email_list)),
+ 'attachment_ids': [(4, attachment.id)]
+ }
+ template.send_mail(record.id, email_values=email_values, force_send=True)
# @api.model
# def create(self, vals):
diff --git a/indoteknik_custom/models/account_move_line.py b/indoteknik_custom/models/account_move_line.py
index 7c95d4ef..5edea25f 100644
--- a/indoteknik_custom/models/account_move_line.py
+++ b/indoteknik_custom/models/account_move_line.py
@@ -1,5 +1,5 @@
from odoo import models, api, fields
-
+from odoo.exceptions import AccessError, UserError, ValidationError
class AccountMoveLine(models.Model):
_inherit = "account.move.line"
@@ -9,6 +9,69 @@ class AccountMoveLine(models.Model):
analytic_account_ids = fields.Many2many('account.analytic.account', string='Analytic Account')
line_no = fields.Integer('No', default=0)
+ def action_gl_reconcile(self):
+ lines = self
+
+ journal = self.env['account.journal'].search([
+ ('suspense_account_id', '=', lines[0].account_id.id)
+ ], limit=1)
+
+ if not journal:
+ raise UserError('Journal dengan suspense account ini tidak ditemukan!')
+
+ statement = self.env['account.bank.statement'].create({
+ 'journal_id': journal.id,
+ 'name': f'REKONSIL {journal.name} {lines[0].date.strftime("%d-%m-%Y")}',
+ 'company_id': self.env.company.id,
+ 'date': lines[0].date,
+ })
+
+ widget_vals = []
+ st_line_ids = []
+
+ for line in lines:
+ amount = line.debit - line.credit
+
+ st_line = self.env['account.bank.statement.line'].create({
+ 'statement_id': statement.id,
+ 'date': line.date or fields.Date.today(),
+ 'payment_ref': line.name,
+ 'partner_id': line.partner_id.id,
+ 'amount': amount,
+ 'ref': line.name,
+ })
+
+ st_line_ids.append(st_line.id)
+
+ widget_vals.append({
+ 'partner_id': st_line.partner_id.id,
+ 'counterpart_aml_dicts': [{
+ 'counterpart_aml_id': line.id,
+ 'debit': abs(amount) if amount < 0 else 0,
+ 'credit': abs(amount) if amount > 0 else 0,
+ 'name': line.name or '/',
+ }],
+ 'payment_aml_ids': [],
+ 'new_aml_dicts': [],
+ 'to_check': False,
+ })
+
+ statement.button_post()
+
+ self.env['account.reconciliation.widget'].process_bank_statement_line(
+ st_line_ids,
+ widget_vals
+ )
+ # statement.button_validate_or_action()
+
+ return {
+ 'effect': {
+ 'fadeout': 'slow',
+ 'message': 'Statement + Auto Reconcile sukses besar 😎🔥',
+ 'type': 'rainbow_man',
+ }
+ }
+
@api.onchange('account_id')
def _onchange_account_id(self):
for account in self:
diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py
index ed0b0809..8cadb1b6 100644
--- a/indoteknik_custom/models/advance_payment_request.py
+++ b/indoteknik_custom/models/advance_payment_request.py
@@ -641,10 +641,16 @@ class AdvancePaymentRequest(models.Model):
today = date.today()
for rec in self:
- current_days = rec.days_remaining or 0
- current_due_date = rec.estimated_return_date or False
- if rec.type_request == 'pum':
- is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids)
+ # current_days = rec.days_remaining or 0
+ # current_due_date = rec.estimated_return_date or False
+ current_days = 0
+ current_due_date = False
+
+ is_settlement_approved = any(s.status == 'approved' for s in rec.settlement_ids)
+ is_pum_canceled = (rec.status == 'cancel')
+
+ if rec.type_request == 'pum' and not is_pum_canceled and not is_settlement_approved:
+
if not is_settlement_approved:
due_date = False
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index e16c8d61..b3ecca56 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -53,6 +53,7 @@ class PurchaseOrder(models.Model):
total_so_percent_margin = fields.Float(
'SO Margin%', compute='compute_total_margin',
help="Total % Margin in Sales Order Header")
+ amount_cashback = fields.Float('Cashback', compute = 'compute_total_margin', help = 'Total Cashback brand Altama')
amount_total_without_service = fields.Float('AmtTotalWithoutService', compute='compute_amt_total_without_service')
summary_qty_po = fields.Float('Total Qty', compute='_compute_summary_qty')
summary_qty_receipt = fields.Float('Summary Qty Receipt', compute='_compute_summary_qty')
@@ -1410,19 +1411,36 @@ class PurchaseOrder(models.Model):
real_item_margin = sales_price - purchase_price
sum_margin += real_item_margin
+ cashback_amount = 0
+ if self.partner_id.id == 5571:
+ cashback_percent = line.product_id.x_manufacture.cashback_percent or 0.0
+ if cashback_percent > 0:
+ cashback_amount = purchase_price * cashback_percent
+ purchase_price -= cashback_amount
+
+ # line.amount_cashback = cashback_amount
+
if sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0:
self.total_so_margin = sum_so_margin
self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100
self.total_margin = sum_margin
self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100
+ self.amount_cashback = 0
+ elif self.partner_id.id == 5571 and sum_so_margin != 0 and sum_sales_price != 0 and sum_margin != 0 and cashback_amount != 0:
+ self.total_so_margin = sum_so_margin
+ self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100
+ self.total_margin = sum_margin
+ self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100
+ self.amount_cashback = cashback_amount
else:
self.total_margin = 0
self.total_percent_margin = 0
self.total_so_margin = 0
self.total_so_percent_margin = 0
+ self.amount_cashback = 0
def compute_total_margin_from_apo(self):
- sum_so_margin = sum_sales_price = sum_margin = 0
+ sum_so_margin = sum_sales_price = sum_margin = cashback_amount = 0
for line in self.order_sales_match_line:
po_line = self.env['purchase.order.line'].search([
('product_id', '=', line.product_id.id),
@@ -1459,18 +1477,27 @@ class PurchaseOrder(models.Model):
if line.purchase_order_id.delivery_amt > 0:
purchase_price += line.purchase_order_id.delivery_amt
+ if self.partner_id.id == 5571:
+ cashback_percent = line.product_id.x_manufacture.cashback_percent or 0.0
+ if cashback_percent > 0:
+ cashback_amount = purchase_price * cashback_percent
+ purchase_price -= cashback_amount
+
real_item_margin = sales_price - purchase_price
sum_margin += real_item_margin
+ self.amount_cashback = cashback_amount
# Akumulasi hasil akhir
if sum_sales_price != 0:
self.total_so_margin = sum_so_margin
self.total_so_percent_margin = round((sum_so_margin / sum_sales_price), 2) * 100
self.total_margin = sum_margin
self.total_percent_margin = round((sum_margin / sum_sales_price), 2) * 100
+ self.amount_cashback = cashback_amount
else:
self.total_margin = self.total_percent_margin = 0
self.total_so_margin = self.total_so_percent_margin = 0
+ self.amount_cashback = 0
def compute_amt_total_without_service(self):
diff --git a/indoteknik_custom/models/purchase_order_line.py b/indoteknik_custom/models/purchase_order_line.py
index 8c72887d..76dcc09e 100755
--- a/indoteknik_custom/models/purchase_order_line.py
+++ b/indoteknik_custom/models/purchase_order_line.py
@@ -23,6 +23,9 @@ class PurchaseOrderLine(models.Model):
so_item_percent_margin = fields.Float(
'SO Margin%', compute='compute_item_margin',
help="Total % Margin in Sales Order Header")
+ amount_cashback = fields.Float(
+ 'SO Margin%', compute='_compute_cashback_brand',
+ help="Total % Margin in Sales Order Header")
delivery_amt_line = fields.Float('DeliveryAmtLine', compute='compute_delivery_amt_line')
line_no = fields.Integer('No', default=0)
qty_available = fields.Float('Qty Available', compute='_compute_qty_stock')
@@ -373,6 +376,9 @@ class PurchaseOrderLine(models.Model):
purchase_price = line.price_subtotal
if order.delivery_amount > 0:
purchase_price += line.delivery_amt_line
+
+ if line.amount_cashback > 0:
+ purchase_price = purchase_price - line.amount_cashback
# Hitung margin dan persentase margin
real_item_margin = total_sales_price - purchase_price
@@ -384,6 +390,46 @@ class PurchaseOrderLine(models.Model):
sum_margin += real_item_margin
+ def _compute_cashback_brand(self):
+ start_date = datetime(2026, 2, 1, 0, 0, 0)
+
+ for line in self:
+ line.amount_cashback = 0.0
+
+ product = line.product_id
+ order = line.order_id
+
+ if not product or not order:
+ continue
+
+ if order.partner_id.id != 5571:
+ continue
+
+ sales_matches = self.env['purchase.order.sales.match'].search([
+ ('purchase_order_id', '=', order.id),
+ ('product_id', '=', product.id)
+ ])
+
+ total_cashback = 0.0
+
+ for match in sales_matches:
+ so_line = match.sale_line_id
+ so_order = so_line.order_id
+
+ if not so_order.date_order or so_order.date_order < start_date:
+ continue
+
+ cashback_percent = product.x_manufacture.cashback_percent or 0.0
+ if cashback_percent <= 0:
+ continue
+ sales_price = so_line.price_reduce_taxexcl * match.qty_so
+
+ cashback = sales_price * cashback_percent
+ total_cashback += cashback
+
+ line.amount_cashback = total_cashback
+
+
def compute_delivery_amt_line(self):
for line in self:
if line.product_id.type == 'product':
diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py
index 646376ab..4c3ca52e 100644
--- a/indoteknik_custom/models/refund_sale_order.py
+++ b/indoteknik_custom/models/refund_sale_order.py
@@ -62,7 +62,8 @@ class RefundSaleOrder(models.Model):
('uang', 'Refund Lebih Bayar'),
('retur_half', 'Refund Retur Sebagian'),
('retur', 'Refund Retur Full'),
- ('salah_transfer', 'Salah Transfer')
+ ('salah_transfer', 'Salah Transfer'),
+ ('berita_acara', 'Kebutuhan Berita Acara')
], string='Refund Type', required=True)
tukar_guling_ids = fields.One2many(
@@ -242,7 +243,7 @@ class RefundSaleOrder(models.Model):
)
invoices = sale_orders.mapped('invoice_ids').filtered(
- lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.payment_state == 'paid'
+ lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state == 'posted'
)
if invoices:
vals['invoice_ids'] = [(6, 0, invoices.ids)]
@@ -251,7 +252,7 @@ class RefundSaleOrder(models.Model):
invoice_ids_data = vals.get('invoice_ids', [])
invoice_ids = invoice_ids_data[0][2] if invoice_ids_data and invoice_ids_data[0][0] == 6 else []
invoices = self.env['account.move'].browse(invoice_ids)
- if invoice_ids and refund_type and refund_type not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half']:
+ if invoice_ids and refund_type and refund_type not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half', 'berita_acara']:
raise UserError("Refund type Hanya Bisa Lebih Bayar, Barang Kosong Sebagian, atau Retur jika ada invoice")
if not invoice_ids and refund_type and refund_type in ['uang', 'barang_kosong_sebagian', 'retur_half']:
@@ -434,13 +435,17 @@ class RefundSaleOrder(models.Model):
total_invoice = sum(self.env['account.move'].browse(invoice_ids).mapped('amount_total_signed')) if invoice_ids else 0.0
vals['total_invoice'] = total_invoice
amount_refund = vals.get('amount_refund', 0.0)
- can_refund = sisa_uang_masuk - total_invoice
-
- if amount_refund > can_refund or can_refund == 0.0:
- raise ValidationError(
- _("Maksimal refund yang bisa dilakukan adalah sebesar %s. "
- "Silakan sesuaikan jumlah refund.") % (can_refund)
- )
+ can_refund = 0.0
+ if refund_type == 'berita_acara':
+ can_refund = sisa_uang_masuk
+ else:
+ can_refund = sisa_uang_masuk - total_invoice
+ if refund_type != 'berita_acara':
+ if amount_refund > can_refund or can_refund == 0.0:
+ raise ValidationError(
+ _("Maksimal refund yang bisa dilakukan adalah sebesar %s. "
+ "Silakan sesuaikan jumlah refund.") % (can_refund)
+ )
if amount_refund <= 0.00:
raise ValidationError('Total Refund harus lebih dari 0 jika ingin mengajukan refund')
@@ -492,7 +497,7 @@ class RefundSaleOrder(models.Model):
valid_invoices = sale_orders.mapped('invoice_ids').filtered(
- lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.payment_state == 'paid'
+ lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state == 'posted'
)
vals['invoice_ids'] = [(6, 0, valid_invoices.ids)]
vals['ongkir'] = sum(so.delivery_amt or 0.0 for so in sale_orders)
@@ -535,7 +540,7 @@ class RefundSaleOrder(models.Model):
else:
invoice_ids = rec.invoice_ids.ids
- if invoice_ids and vals.get('refund_type', rec.refund_type) not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half', 'retur']:
+ if invoice_ids and vals.get('refund_type', rec.refund_type) not in ['uang', 'barang_kosong_sebagian', 'barang_kosong', 'retur_half', 'retur', 'berita_acara']:
raise UserError("Refund type Hanya Bisa Lebih Bayar, Barang Kosong Sebagian, atau Retur jika ada invoice")
if not invoice_ids and vals.get('refund_type', rec.refund_type) in ['uang', 'barang_kosong_sebagian', 'retur_half']:
@@ -548,10 +553,28 @@ class RefundSaleOrder(models.Model):
if any(field in vals for field in ['uang_masuk', 'invoice_ids', 'ongkir', 'sale_order_ids', 'amount_refund']):
total_invoice = sum(self.env['account.move'].browse(invoice_ids).mapped('amount_total_signed'))
vals['total_invoice'] = total_invoice
- uang_masuk = rec.uang_masuk
- can_refund = uang_masuk - total_invoice
-
+ uang_masuk = vals.get('uang_masuk', rec.uang_masuk)
amount_refund = vals.get('amount_refund', rec.amount_refund)
+ can_refund = 0.0
+ total_refunded = 0.0
+
+ if refund_type == 'berita_acara':
+ can_refund = uang_masuk
+ remaining = uang_masuk - amount_refund
+ else:
+ can_refund = uang_masuk - total_invoice
+
+ existing_refunds = self.search([
+ ('sale_order_ids', 'in', so_ids),
+ ('id', '!=', rec.id)
+ ])
+ total_refunded = sum(existing_refunds.mapped('amount_refund'))
+
+ if existing_refunds:
+ remaining = uang_masuk - total_refunded
+ else:
+ remaining = uang_masuk - amount_refund
+
if amount_refund > can_refund:
raise ValidationError(
@@ -601,7 +624,7 @@ class RefundSaleOrder(models.Model):
for rec in self:
move_links = []
- invoice_ids = rec.sale_order_ids.mapped('invoice_ids')
+ invoice_ids = rec.sale_order_ids.mapped('invoice_ids').filtered(lambda m: m.state == 'posted')
moves = self.env['account.move'].search([
('sale_id', 'in', rec.sale_order_ids.ids),
@@ -708,7 +731,7 @@ class RefundSaleOrder(models.Model):
for so in self.sale_order_ids:
self.ongkir += so.delivery_amt or 0.0
valid_invoices = so.invoice_ids.filtered(
- lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.payment_state == 'paid'
+ lambda inv: inv.move_type in ['out_invoice', 'out_refund'] and inv.state == 'posted'
)
all_invoices |= valid_invoices
total_invoice += sum(valid_invoices.mapped('amount_total_signed'))
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 469509d4..2548c7b3 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -2316,7 +2316,7 @@ class SaleOrder(models.Model):
for order in self:
for line in order.order_line:
search_product = self.env['sale.order.line'].search(
- [('product_id', '=', line.product_id.id), ('order_id', '=', order.id)])
+ [('product_id', '=', line.product_id.id), ('order_id', '=', order.id), ('display_type', '=', False)])
if len(search_product) > 1:
raise UserError("Terdapat DUPLIKASI data pada Product {}".format(line.product_id.display_name))
@@ -2598,6 +2598,18 @@ class SaleOrder(models.Model):
else:
return False
+ def check_archived_product(self):
+ for order in self:
+ for line in order.order_line:
+ if line.product_id.active == False:
+ raise UserError("Terdapat Product yang sudah di Archive pada Product: {}".format(line.product_id.display_name))
+
+ def check_archived_uom(self):
+ for order in self:
+ for line in order.order_line:
+ if line.product_uom.active == False:
+ raise UserError("Terdapat UoM yang sudah di Archive pada UoM {} di Product {}".format(line.product_uom.name, line.product_id.display_name))
+
def action_confirm(self):
for order in self:
order._validate_delivery_amt()
@@ -2614,6 +2626,8 @@ class SaleOrder(models.Model):
order._validate_order()
order._validate_npwp()
order.order_line.validate_line()
+ order.check_archived_product()
+ order.check_archived_uom()
main_parent = order.partner_id.get_main_parent()
SYSTEM_UID = 25
@@ -3438,7 +3452,7 @@ class SaleOrder(models.Model):
def button_refund(self):
self.ensure_one()
- invoice_ids = self.invoice_ids.filtered(lambda inv: inv.payment_state == 'paid')
+ invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state == 'posted')
moves = self.env['account.move'].search([
('sale_id', '=', self.id),
diff --git a/indoteknik_custom/models/sale_order_line.py b/indoteknik_custom/models/sale_order_line.py
index 1df1a058..dd44f84a 100644
--- a/indoteknik_custom/models/sale_order_line.py
+++ b/indoteknik_custom/models/sale_order_line.py
@@ -17,6 +17,7 @@ class SaleOrderLine(models.Model):
help="Total % Margin in Sales Order Header")
item_percent_margin_before = fields.Float('%Margin Before', compute='_compute_item_percent_margin_before',
help="Total % Margin excluding third party in Sales Order Header")
+ amount_cashback = fields.Float('Cashback Brand', compute='_compute_cashback_brand', help='Cashback from product who has cashback percent in manufacture')
initial_discount = fields.Float('Initial Discount')
vendor_id = fields.Many2one(
'res.partner', string='Vendor', readonly=True,
@@ -164,7 +165,10 @@ class SaleOrderLine(models.Model):
purchase_price = line.purchase_price
if line.purchase_tax_id.price_include:
- purchase_price = line.purchase_price / 1.11
+ purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100))
+
+ if line.amount_cashback > 0:
+ purchase_price = purchase_price - line.amount_cashback
purchase_price = purchase_price * line.product_uom_qty
margin_per_item = sales_price - purchase_price
@@ -186,7 +190,10 @@ class SaleOrderLine(models.Model):
purchase_price = line.purchase_price
if line.purchase_tax_id and line.purchase_tax_id.price_include:
- purchase_price = line.purchase_price / 1.11
+ purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100))
+
+ if line.amount_cashback > 0:
+ purchase_price = purchase_price - line.amount_cashback
purchase_price = purchase_price * line.product_uom_qty
@@ -215,7 +222,10 @@ class SaleOrderLine(models.Model):
purchase_price = line.purchase_price
if line.purchase_tax_id.price_include:
- purchase_price = line.purchase_price / 1.11
+ purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100))
+
+ if line.amount_cashback > 0:
+ purchase_price = purchase_price - line.amount_cashback
purchase_price = purchase_price * line.product_uom_qty
margin_per_item = sales_price - purchase_price
@@ -241,12 +251,47 @@ class SaleOrderLine(models.Model):
purchase_price = line.purchase_price
if line.purchase_tax_id.price_include:
- purchase_price = line.purchase_price / 1.11
+ purchase_price = line.purchase_price / (1 + (line.purchase_tax_id.amount / 100))
+
+ if line.amount_cashback > 0:
+ purchase_price = purchase_price - line.amount_cashback
purchase_price = purchase_price * line.product_uom_qty
margin_per_item = sales_price - purchase_price
line.item_before_margin = margin_per_item
+ def _compute_cashback_brand(self):
+ start_date = datetime(2026, 2, 1, 0, 0, 0)
+ for line in self:
+ line.amount_cashback = 0
+
+ if not line.product_id:
+ continue
+
+ if line.order_id.date_order < start_date:
+ continue
+
+ price, taxes, vendor_id = self._get_purchase_price(line.product_id)
+
+ cashback_percent = line.product_id.x_manufacture.cashback_percent or 0
+ if cashback_percent <= 0:
+ continue
+
+
+ if line.vendor_id.id != 5571:
+ continue
+
+ price_tax_excl = price
+
+ if taxes:
+ tax = self.env['account.tax'].browse(taxes)
+ if tax.price_include:
+ price_tax_excl = price / (1 + (tax.amount / 100))
+ else:
+ price_tax_excl = price
+
+ line.amount_cashback = price_tax_excl * cashback_percent
+
# @api.onchange('vendor_id')
# def onchange_vendor_id(self):
# # TODO : need to change this logic @stephan
diff --git a/indoteknik_custom/models/shipment_group.py b/indoteknik_custom/models/shipment_group.py
index 7203b566..ce4a9fdd 100644
--- a/indoteknik_custom/models/shipment_group.py
+++ b/indoteknik_custom/models/shipment_group.py
@@ -2,6 +2,7 @@ from odoo import models, api, fields
from odoo.exceptions import AccessError, UserError, ValidationError
from datetime import timedelta, date
import logging
+from markupsafe import escape as html_escape
_logger = logging.getLogger(__name__)
@@ -16,7 +17,21 @@ class ShipmentGroup(models.Model):
partner_id = fields.Many2one('res.partner', string='Customer')
carrier_id = fields.Many2one('delivery.carrier', string='Ekspedisi')
total_colly_line = fields.Float(string='Total Colly', compute='_compute_total_colly_line')
+ is_multi_partner = fields.Boolean(string='Is Multi Partner', compute='_compute_is_multi_partner')
+ partner_ids = fields.Many2many('res.partner', string='Customers', compute='_compute_partner_ids')
+ driver = fields.Many2one('res.users', string='Driver')
+ @api.depends('shipment_line.partner_id')
+ def _compute_partner_ids(self):
+ for rec in self:
+ rec.partner_ids = rec.shipment_line.mapped('partner_id').ids
+
+ @api.depends('shipment_line.partner_id')
+ def _compute_is_multi_partner(self):
+ for rec in self:
+ partners = rec.shipment_line.mapped('partner_id')
+ rec.is_multi_partner = len(partners) > 1
+
def sync_api_shipping(self):
for rec in self.shipment_line:
picking_names = [lines.picking_id.name for lines in self.shipment_line]
@@ -97,14 +112,14 @@ class ShipmentGroupLine(models.Model):
@api.onchange('picking_id')
def onchange_picking_id(self):
if self.picking_id:
- picking = self.env['stock.picking'].browse(self.picking_id.id)
+ picking = self.picking_id
if self.shipment_id.carrier_id and self.shipment_id.carrier_id != picking.carrier_id:
raise UserError('carrier must be same as shipment group')
-
+
if picking.total_mapping_koli == 0:
raise UserError(f'Picking {picking.name} tidak memiliki mapping koli')
-
+
self.partner_id = picking.partner_id
self.shipping_paid_by = picking.sale_id.shipping_paid_by
self.carrier_id = picking.carrier_id.id
@@ -115,6 +130,9 @@ class ShipmentGroupLine(models.Model):
self.sale_id = picking.sale_id
+ if self.shipment_id:
+ self.shipment_id._compute_is_multi_partner()
+
@api.model
def create(self, vals):
record = super(ShipmentGroupLine, self).create(vals)
diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py
index 84eb5a17..cb7d3773 100644
--- a/indoteknik_custom/models/stock_inventory.py
+++ b/indoteknik_custom/models/stock_inventory.py
@@ -16,6 +16,11 @@ class StockInventory(models.Model):
('in', 'Adjusment In'),
('out', 'Adjusment Out'),
], string='Adjusments Type', required=True)
+ approval_state = fields.Selection([
+ ('logistic', 'Logistic'),
+ ('accounting', 'Accounting'),
+ ('approved', 'Approved'),
+ ], default='logistic', tracking=True)
def _generate_number_stock_inventory(self):
"""Men-generate nomor untuk semua stock inventory yang belum memiliki number."""
@@ -53,8 +58,11 @@ class StockInventory(models.Model):
return "00001" # Jika belum ada data, mulai dari 00001
def action_start(self):
- if self.env.user.id not in [21, 17, 571, 28]:
- raise UserError("Hanya Rafly, Denise, Iqmal, dan Stephan yang bisa start inventory")
+ if self.approval_state != 'approved' and self.adjusment_type == 'out':
+ raise UserError('Harus melalui proses approval')
+ if self.adjusment_type == 'in':
+ if self.env.user.id not in [21, 17, 571, 28]:
+ raise UserError("Hanya Rafly, Denise, Iqmal, dan Stephan yang bisa start inventory")
return super(StockInventory, self).action_start()
@api.model
@@ -69,6 +77,20 @@ class StockInventory(models.Model):
self._assign_number(order) # Generate number setelah save
return order
+
+ def action_approve(self):
+ if self.adjusment_type == 'out':
+ for rec in self:
+ if rec.approval_state == 'logistic':
+ if not rec.env.user.has_group('indoteknik_custom.group_role_logistic'):
+ raise UserError("Harus diapprove logistic")
+ rec.approval_state = 'accounting'
+ elif rec.approval_state == 'accounting':
+ if not rec.env.user.has_group('indoteknik_custom.group_role_fat'):
+ raise UserError("Harus diapprove accounting")
+ rec.approval_state = 'approved'
+ else:
+ raise UserError("Sudah Approved")
def write(self, vals):
"""Jika adjusment_type diubah, generate ulang nomor."""
diff --git a/indoteknik_custom/models/stock_picking.py b/indoteknik_custom/models/stock_picking.py
index 2465fa96..065b1484 100644
--- a/indoteknik_custom/models/stock_picking.py
+++ b/indoteknik_custom/models/stock_picking.py
@@ -203,6 +203,16 @@ class StockPicking(models.Model):
is_so_fiktif = fields.Boolean('SO Fiktif?', compute='_compute_is_so_fiktif', tracking=3)
payment_term = fields.Char('Payment Term', compute='_get_partner_payment_term')
is_rev_tg = fields.Boolean('Administrasi')
+ shipment_group_id = fields.Many2one('shipment.group', string='Shipment Group', compute='_compute_shipment_group_id')
+
+ @api.depends('shipment_group_id')
+ def _compute_shipment_group_id(self):
+ for record in self:
+ shipment_line = self.env['shipment.group.line'].search([('picking_id', '=', record.id)], limit=1)
+ if shipment_line:
+ record.shipment_group_id = shipment_line.shipment_id.id
+ else:
+ record.shipment_group_id = False
@api.depends('sale_id.payment_term_id')
def _get_partner_payment_term(self):
@@ -1387,7 +1397,8 @@ class StockPicking(models.Model):
])
if quant:
- return quant.quantity
+ return sum(quant.mapped('quantity'))
+ # return quant.quantity
return 0
diff --git a/indoteknik_custom/models/tukar_guling_po.py b/indoteknik_custom/models/tukar_guling_po.py
index ae58d509..1ee10679 100644
--- a/indoteknik_custom/models/tukar_guling_po.py
+++ b/indoteknik_custom/models/tukar_guling_po.py
@@ -582,7 +582,23 @@ class TukarGulingPO(models.Model):
('group_id', '=', group.id),
('state', '=', 'done')
])
- bu_inputs = po_pickings.filtered(lambda p: p.picking_type_id.id == 28)
+
+ product_ids = set(record.line_ids.mapped("product_id").ids)
+
+ _logger.info("TG product_ids: %s", product_ids)
+
+ def _get_moves(picking):
+ return picking.move_ids_without_package if picking.move_ids_without_package else picking.move_lines
+
+ bu_inputs = po_pickings.filtered(
+ lambda p: p.picking_type_id.id == 28 and any(
+ m.product_id.id in product_ids
+ for m in _get_moves(p)
+ )
+ )
+
+ _logger.info("BU INPUT dengan product sama: %s", bu_inputs.mapped("name"))
+
bu_puts = po_pickings.filtered(lambda p: p.picking_type_id.id == 75)
else:
raise UserError("Group ID tidak ditemukan pada BU Operations.")
@@ -711,12 +727,26 @@ class TukarGulingPO(models.Model):
# Ambil pasangannya di BU INPUT (asumsi urutan sejajar)
sorted_bu_puts = sorted(bu_puts, key=lambda p: p.name)
+ # sorted_bu_inputs = sorted(bu_inputs, key=lambda p: p.name)
+
+ # if bu_put_index >= len(sorted_bu_inputs):
+ # raise UserError("Tidak ditemukan pasangan BU INPUT untuk BU PUT yang dipilih.")
+
+ # paired = [(sorted_bu_puts[bu_put_index], sorted_bu_inputs[bu_put_index])]
sorted_bu_inputs = sorted(bu_inputs, key=lambda p: p.name)
- if bu_put_index >= len(sorted_bu_inputs):
- raise UserError("Tidak ditemukan pasangan BU INPUT untuk BU PUT yang dipilih.")
+ if not sorted_bu_inputs:
+ raise UserError(
+ "Tidak ditemukan BU INPUT yang memiliki product TG."
+ )
- paired = [(sorted_bu_puts[bu_put_index], sorted_bu_inputs[bu_put_index])]
+ paired = [(record.operations, sorted_bu_inputs[0])]
+
+ _logger.info(
+ "🔗 Pairing BU PUT %s dengan BU INPUT %s",
+ record.operations.name,
+ sorted_bu_inputs[0].name
+ )
for bu_put, bu_input in paired:
vrt = _create_return_from_picking(bu_put, bu_put_qty_map)
diff --git a/indoteknik_custom/models/update_depreciation_move_wizard.py b/indoteknik_custom/models/update_depreciation_move_wizard.py
new file mode 100644
index 00000000..7d465f1d
--- /dev/null
+++ b/indoteknik_custom/models/update_depreciation_move_wizard.py
@@ -0,0 +1,48 @@
+from odoo import models, fields, api
+from odoo.exceptions import UserError
+
+class UpdateDepreciationMoveWizard(models.TransientModel):
+ _name = 'update.depreciation.move.wizard'
+ _description = 'Wizard untuk Update Move Check Depreciation Line'
+
+ target_date = fields.Date(string="Tanggal Depresiasi", required=True)
+
+ # def action_update_move_check(self):
+ # lines = self.env['account.asset.depreciation.line'].search([
+ # ('depreciation_date', '=', self.target_date),
+ # ])
+ # if not lines:
+ # raise UserError("Tidak ada baris depresiasi dengan tanggal tersebut.")
+
+ # updated_count = 0
+ # for line in lines:
+ # if not line.move_check:
+ # line.move_check = True
+ # line.move_posted_check = True
+ # updated_count += 1
+
+ # return {
+ # 'type': 'ir.actions.client',
+ # 'tag': 'display_notification',
+ # 'params': {
+ # 'title': 'Update Selesai',
+ # 'message': f'{updated_count} baris berhasil di-update.',
+ # 'type': 'success',
+ # 'sticky': False,
+ # }
+ # }
+
+ def action_update_move_check(self):
+ assets = self.env['account.asset.asset']
+ assets.compute_generated_entries(self.target_date)
+
+ return {
+ 'type': 'ir.actions.client',
+ 'tag': 'display_notification',
+ 'params': {
+ 'title': 'Update Selesai',
+ 'message': 'Depresiasi berhasil di-update.',
+ 'type': 'success',
+ 'sticky': False,
+ }
+ } \ No newline at end of file
diff --git a/indoteknik_custom/models/x_manufactures.py b/indoteknik_custom/models/x_manufactures.py
index 9e214d92..0c3bfa3b 100755
--- a/indoteknik_custom/models/x_manufactures.py
+++ b/indoteknik_custom/models/x_manufactures.py
@@ -50,7 +50,7 @@ class XManufactures(models.Model):
# user_id = fields.Many2one('res.users', string='Responsible', domain="['|'('id', '=', 19), ('id', '=', 6)]", help="Siapa yang bertanggung jawab")
user_id = fields.Many2one('res.users', string='Responsible', help="Siapa yang bertanggung jawab")
override_vendor_id = fields.Many2one('res.partner', string='Override Vendor')
- # cashback_percent = fields.Float(string='Cashback Percent')
+ cashback_percent = fields.Float(string='Cashback Percent', default=0)
def _compute_vendor_ids(self):
for manufacture in self: