summaryrefslogtreecommitdiff
path: root/indoteknik_custom/models
diff options
context:
space:
mode:
authorMqdd <ahmadmiqdad27@gmail.com>2026-02-17 16:40:42 +0700
committerMqdd <ahmadmiqdad27@gmail.com>2026-02-17 16:40:42 +0700
commit7c893b6cfeae88c82231f682517ae143b4cf580b (patch)
treeb249c3d3a462d4817a39ff00954765f3a4f7788f /indoteknik_custom/models
parent8bb3696e35dc122ea153566834a48ec0659e0101 (diff)
parenta461efcda1637c267b3c4195eb0f7d649f10f145 (diff)
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into cr_renca_find
Diffstat (limited to 'indoteknik_custom/models')
-rwxr-xr-xindoteknik_custom/models/__init__.py1
-rw-r--r--indoteknik_custom/models/account_move.py100
-rw-r--r--indoteknik_custom/models/account_move_line.py65
-rw-r--r--indoteknik_custom/models/advance_payment_request.py62
-rw-r--r--indoteknik_custom/models/automatic_purchase.py3
-rw-r--r--indoteknik_custom/models/commision.py5
-rw-r--r--indoteknik_custom/models/price_group.py4
-rwxr-xr-xindoteknik_custom/models/product_public_category.py1
-rwxr-xr-xindoteknik_custom/models/product_template.py11
-rwxr-xr-xindoteknik_custom/models/purchase_order.py44
-rwxr-xr-xindoteknik_custom/models/purchase_order_line.py46
-rw-r--r--indoteknik_custom/models/purchasing_job.py4
-rw-r--r--indoteknik_custom/models/purchasing_job_multi_update.py17
-rw-r--r--indoteknik_custom/models/purchasing_job_state.py3
-rw-r--r--indoteknik_custom/models/refund_sale_order.py89
-rwxr-xr-xindoteknik_custom/models/sale_order.py31
-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/solr/apache_solr.py16
-rw-r--r--indoteknik_custom/models/solr/product_product.py2
-rw-r--r--indoteknik_custom/models/stock_inventory.py44
-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.py1
25 files changed, 617 insertions, 108 deletions
diff --git a/indoteknik_custom/models/__init__.py b/indoteknik_custom/models/__init__.py
index 328c76b0..a042750b 100755
--- a/indoteknik_custom/models/__init__.py
+++ b/indoteknik_custom/models/__init__.py
@@ -165,4 +165,5 @@ from . import partial_delivery
from . import domain_apo
from . import uom_uom
from . import commission_internal
+from . import update_depreciation_move_wizard
from . import keywords
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index 37808f94..42467b78 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -49,7 +49,7 @@ class AccountMove(models.Model):
states={'draft': [('readonly', False)], 'sent': [('readonly', False)], 'sale': [('readonly', False)]},
domain="['|', ('company_id', '=', False), ('company_id', '=', company_id)]",
help="Dipakai untuk alamat tempel")
-
+
address_invoice = fields.Char(related='real_invoice_id.street', string='Invoice Address', readonly=True)
bills_efaktur_exporter = fields.Many2one('res.users', string='Efaktur Exporter')
bills_date_efaktur = fields.Datetime(string="eFaktur Exported Date", required=False)
@@ -176,7 +176,7 @@ class AccountMove(models.Model):
lambda p: move.id in p.reconciled_invoice_ids.ids
)
- if payment:
+ if payment:
move.payment_date = payment[0].date
elif move.reklas_misc_id:
move.payment_date = move.reklas_misc_id.date
@@ -185,7 +185,7 @@ class AccountMove(models.Model):
def action_sync_promise_date(self):
self.ensure_one()
- finance_user_ids = [688]
+ finance_user_ids = [688]
is_it = self.env.user.has_group('indoteknik_custom.group_role_it')
if self.env.user.id not in finance_user_ids and not is_it:
raise UserError('Hanya Finance (Widya) yang dapat menggunakan fitur ini.')
@@ -286,7 +286,7 @@ class AccountMove(models.Model):
if all(inv.reminder_sent_date == today for inv in invs):
_logger.info(f"Reminder untuk {partner.name} sudah terkirim hari ini, skip.")
continue
-
+
promise_dates = [inv.customer_promise_date for inv in invs if inv.customer_promise_date]
if promise_dates:
earliest_promise = min(promise_dates) # ambil janji paling awal
@@ -300,11 +300,11 @@ class AccountMove(models.Model):
# skip semua jika partner centang dont_send_reminder_inv_all
if partner.dont_send_reminder_inv_all:
_logger.info(f"Partner {partner.name} skip karena dont_send_reminder_inv_all aktif")
- continue
+ continue
# cek parent hanya dengan flag dont_sent_reminder_inv_parent
if not partner.dont_send_reminder_inv_parent and partner.email:
emails.append(partner.email)
-
+
# Ambil child contact yang di-checklist reminder_invoices
reminder_contacts = self.env['res.partner'].search([
('parent_id', '=', partner.id),
@@ -312,7 +312,7 @@ class AccountMove(models.Model):
('email', '!=', False),
])
_logger.info(f"Email Reminder Child {reminder_contacts}")
-
+
emails += reminder_contacts.mapped('email')
if reminder_contacts:
_logger.info(f"Email Reminder Child {reminder_contacts}")
@@ -381,7 +381,7 @@ class AccountMove(models.Model):
# tempo_link = 'http://localhost:2100/my/tempo'
# payment_term = partner.previous_payment_term_id if partner.is_cbd_locked else partner.property_payment_term_id
payment_term = invs[0].invoice_payment_term_id
-
+
limit_info_html = f"""
<p><b>Informasi Tambahan:</b></p>
<ul style="font-size:12px; color:#333; line-height:1.4; margin:0; padding-left:0; list-style-position:inside;">
@@ -391,11 +391,11 @@ class AccountMove(models.Model):
Sisa Kredit Limit: {formatLang(self.env, blocking_limit - outstanding_amount, currency_obj=currency)}
</li>
<li style="color:red;">
- Kredit Limit Terpakai: {formatLang(self.env, outstanding_amount, currency_obj=currency)}
+ Kredit Limit Terpakai: {formatLang(self.env, outstanding_amount, currency_obj=currency)}
<span style="font-size:12px; color:#666;">({len(outstanding_invoices)} Transaksi)</span>
</li>
<li style="color:red;">
- Jatuh Tempo: {formatLang(self.env, overdue_amount, currency_obj=currency)}
+ Jatuh Tempo: {formatLang(self.env, overdue_amount, currency_obj=currency)}
<span style="font-size:12px; color:#666;">({len(overdue_invoices)} Invoice)</span>
</li>
</ul>
@@ -472,7 +472,7 @@ class AccountMove(models.Model):
values = {
'subject': f"Reminder Invoice Due - {partner.name}",
# 'email_to': 'andrifebriyadiputra@gmail.com',
- 'email_to': email_to,
+ 'email_to': email_to,
'email_from': 'finance@indoteknik.co.id',
'email_cc': ",".join(sorted(set(cc_list))),
'body_html': body_html,
@@ -528,7 +528,7 @@ class AccountMove(models.Model):
# payment_term = rec.invoice_payment_term_id.line_ids[0].days
# terima_faktur = rec.date_terima_tukar_faktur
# payment = self.search([('ref', '=', rec.name), ('move_type', '=', 'entry')], limit=1)
-
+
# if payment and terima_faktur:
# date_diff = terima_faktur - payment.date
# rec.length_of_payment = date_diff.days + payment_term
@@ -587,14 +587,30 @@ 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:
+ if record.invoice_payment_term_id.id == 26:
attachment = self.generate_attachment(record)
email_values = {
'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):
@@ -605,7 +621,7 @@ class AccountMove(models.Model):
@api.model
def create(self, vals):
- vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0'
+ vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0'
result = super(AccountMove, self).create(vals)
# Tambahan: jika ini Vendor Bill dan tanggal belum diisi
@@ -640,13 +656,13 @@ class AccountMove(models.Model):
record.flag_delivery_amt = True
else:
record.flag_delivery_amt = False
-
+
def compute_delivery_amt_text(self):
tb = Terbilang()
for record in self:
res = ''
-
+
try:
if record.sale_id.delivery_amt > 0:
tb.parse(int(record.sale_id.delivery_amt))
@@ -654,7 +670,7 @@ class AccountMove(models.Model):
record.delivery_amt_text = res + ' Rupiah'
except:
record.delivery_amt_text = res
-
+
@api.constrains('bills_efaktur_document')
def _constrains_efaktur_document(self):
@@ -722,13 +738,35 @@ class AccountMove(models.Model):
raise UserError('Data Hanya Bisa Di Cancel')
return res
+ def copy(self, default=None):
+ default = dict(default or {})
+
+ today = fields.Date.context_today(self)
+
+ if self.invoice_date_due:
+ default.update({
+ 'invoice_date_due': today,
+ })
+
+ move = super().copy(default)
+
+ for line in move.line_ids:
+ if (
+ line.account_id.user_type_id.type in ('receivable', 'payable')
+ and line.date_maturity
+ ):
+ line.date_maturity = today
+
+ return move
+
+
def button_cancel(self):
res = super(AccountMove, self).button_cancel()
if self.move_type == 'entry':
po = self.env['purchase.order'].search([
('move_id', 'in', [self.id])
])
-
+
for order in po:
if order:
order.is_create_uangmuka = False
@@ -741,7 +779,7 @@ class AccountMove(models.Model):
res = super(AccountMove, self).button_draft()
if not self.env.user.is_accounting:
raise UserError('Hanya Accounting yang bisa Reset to Draft')
-
+
for rec in self.line_ids:
if rec.write_date != rec.create_date:
if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm':
@@ -751,7 +789,7 @@ class AccountMove(models.Model):
def action_post(self):
if self._name != 'account.move':
return super(AccountMove, self).action_post()
-
+
# validation cant qty invoice greater than qty order
if self.move_type == 'out_invoice':
query = ["&",("name","=",self.invoice_origin),"|",("state","=","sale"),("state","=","done")]
@@ -777,7 +815,7 @@ class AccountMove(models.Model):
# raise UserError('Hanya Accounting yang bisa Posting')
# if self._name == 'account.move':
# for entry in self:
- # entry.date_completed = datetime.utcnow()
+ # entry.date_completed = datetime.utcnow()
# for line in entry.line_ids:
# line.date_maturity = entry.date
@@ -787,7 +825,7 @@ class AccountMove(models.Model):
if not self.env.user.is_accounting:
raise UserError("Hanya Finence yang bisa ubah data")
return res
-
+
def _compute_invoice_day_to_due(self):
for invoice in self:
invoice_day_to_due = 0
@@ -801,7 +839,7 @@ class AccountMove(models.Model):
new_invoice_day_to_due = new_invoice_day_to_due.days
invoice.invoice_day_to_due = invoice_day_to_due
invoice.new_invoice_day_to_due = new_invoice_day_to_due
-
+
def _compute_bill_day_to_due(self):
for rec in self:
rec.bill_day_to_due = rec.payment_schedule or rec.invoice_date_due
@@ -831,21 +869,21 @@ class AccountMove(models.Model):
add_days += line.days
due_date = tukar_date + timedelta(days=add_days)
invoice.invoice_date_due = due_date
-
+
def open_form_multi_update(self):
action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_account_move_multi_update')
action['context'] = {
'move_ids': [x.id for x in self]
}
return action
-
+
def open_form_multi_update_bills(self):
action = self.env['ir.actions.act_window']._for_xml_id('indoteknik_custom.action_account_move_multi_update_bills')
action['context'] = {
'move_ids': [x.id for x in self]
}
return action
-
+
@api.constrains('efaktur_id', 'ref', 'date', 'journal_id', 'name')
def constrains_edit(self):
for rec in self.line_ids:
@@ -859,10 +897,10 @@ class AccountMove(models.Model):
# if rec.statement_line_id and not rec.statement_line_id.statement_id.is_edit and rec.statement_line_id.statement_id.state == 'confirm':
# raise UserError('Bank Statement di Lock, Minta admin reconcile untuk unlock')
# return res
-
+
def validate_faktur_for_export(self):
- invoices = self.filtered(lambda inv: not inv.is_efaktur_exported and
- inv.state == 'posted' and
+ invoices = self.filtered(lambda inv: not inv.is_efaktur_exported and
+ inv.state == 'posted' and
inv.move_type == 'out_invoice')
invalid_invoices = self - invoices
@@ -874,10 +912,10 @@ class AccountMove(models.Model):
))
return invoices
-
+
def export_faktur_to_xml(self):
valid_invoices = self
-
+
coretax_faktur = self.env['coretax.faktur'].create({})
response = coretax_faktur.export_to_download(
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 f973a9da..8cadb1b6 100644
--- a/indoteknik_custom/models/advance_payment_request.py
+++ b/indoteknik_custom/models/advance_payment_request.py
@@ -55,12 +55,14 @@ class AdvancePaymentRequest(models.Model):
('pengajuan2', 'Menunggu Approval AP'),
('pengajuan3', 'Menunggu Approval Pimpinan'),
('approved', 'Approved'),
+ ('cancel','Cancel')
], string='Status', default='draft', tracking=3, index=True, track_visibility='onchange')
status_pay_down_payment = fields.Selection([
('pending', 'Pending'),
('payment', 'Payment'),
+ ('cancel','Cancel')
], string='Status Pembayaran', default='pending', tracking=3)
name_approval_departement = fields.Char(string='Approval Departement')
@@ -639,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
@@ -816,6 +824,52 @@ class AdvancePaymentRequest(models.Model):
rec._compute_grand_total_reimburse()
rec.nominal = rec.grand_total_reimburse
return rec
+
+ def action_open_cancel_wizard(self):
+ """Membuka Wizard Pop-up untuk Cancel PUM/Reimburse"""
+ self.ensure_one()
+
+ if self.move_id:
+ raise UserError(_("Pengajuan tidak dapat dibatalkan karena Journal sudah terbentuk."))
+
+ if self.settlement_ids and any(s.status != 'draft' for s in self.settlement_ids):
+ raise UserError(_("Pengajuan tidak dapat dibatalkan karena sudah ada proses realisasi."))
+
+ return {
+ 'name': _('Alasan Pembatalan'),
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'advance.payment.cancel.wizard',
+ 'view_mode': 'form',
+ 'target': 'new',
+ 'context': {
+ 'default_request_id': self.id,
+ }
+ }
+
+class AdvancePaymentCancelWizard(models.TransientModel):
+ _name = 'advance.payment.cancel.wizard'
+ _description = 'Wizard untuk Membatalkan PUM/Reimburse'
+
+ request_id = fields.Many2one('advance.payment.request', string='Dokumen', readonly=True)
+ reason = fields.Text(string='Alasan Pembatalan', required=True)
+
+ def action_confirm_cancel(self):
+ self.ensure_one()
+ request = self.request_id
+ if request.move_id:
+ raise UserError("Tidak bisa melakukan cancel karena Jurnal (Move ID) sudah terbentuk.")
+
+ request.write({'status': 'cancel'})
+ request.write({'status_pay_down_payment': 'cancel'})
+
+ request.message_post(
+ body=f"Pengajuan telah <b>DIBATALKAN</b> oleh {self.env.user.name}.<br/>"
+ f"<b>Alasan:</b> {self.reason}",
+ message_type="comment",
+ subtype_xmlid="mail.mt_note",
+ )
+
+ return {'type': 'ir.actions.act_window_close'}
class AdvancePaymentUsageLine(models.Model):
diff --git a/indoteknik_custom/models/automatic_purchase.py b/indoteknik_custom/models/automatic_purchase.py
index 4b0ce325..0b2f7d1b 100644
--- a/indoteknik_custom/models/automatic_purchase.py
+++ b/indoteknik_custom/models/automatic_purchase.py
@@ -499,7 +499,8 @@ class AutomaticPurchase(models.Model):
# _logger.info('test %s' % point.product_id.name)
if point.product_id.qty_available_bandengan > point.product_min_qty:
continue
- qty_purchase = point.product_max_qty - point.product_id.qty_incoming_bandengan - point.product_id.qty_available_bandengan
+ # qty_purchase = point.product_max_qty - point.product_id.qty_incoming_bandengan - point.product_id.qty_available_bandengan
+ qty_purchase = point.product_max_qty - point.product_id.qty_available_bandengan
po_line = self.env['purchase.order.line'].search([('product_id', '=', point.product_id.id), ('order_id.state', '=', 'done')], order='id desc', limit=1)
if self.vendor_id:
diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py
index 441dd54f..983c07fe 100644
--- a/indoteknik_custom/models/commision.py
+++ b/indoteknik_custom/models/commision.py
@@ -489,7 +489,10 @@ class CustomerCommision(models.Model):
raise UserError('Line sudah ada, tidak bisa di generate ulang')
if self.commision_type == 'fee':
- self._generate_customer_commision_fee()
+ if self.brand_ids:
+ self._generate_customer_commision_rebate()
+ else:
+ self._generate_customer_commision_fee()
else:
self._generate_customer_commision_rebate()
diff --git a/indoteknik_custom/models/price_group.py b/indoteknik_custom/models/price_group.py
index 1b1c817f..fce78fff 100644
--- a/indoteknik_custom/models/price_group.py
+++ b/indoteknik_custom/models/price_group.py
@@ -18,6 +18,8 @@ class PriceGroup(models.Model):
group6 = fields.Float(string='Kelompok 6 (%)')
group7 = fields.Float(string='Kelompok 7 (%)')
group8 = fields.Float(string='Kelompok 8 (%)')
+ group9 = fields.Float(string='Kelompok 9 (%)')
+ group10 = fields.Float(string='Kelompok 10 (%)')
def collect_price_group(self):
PRICE_GROUP_ID = {
@@ -47,4 +49,6 @@ class Manufacture(models.Model):
('group6', 'Kelompok 6'),
('group7', 'Kelompok 7'),
('group8', 'Kelompok 8'),
+ ('group9', 'Kelompok 9'),
+ ('group10', 'Kelompok 10'),
], string='Pricing Group', copy=False)
diff --git a/indoteknik_custom/models/product_public_category.py b/indoteknik_custom/models/product_public_category.py
index 1039ec52..dadcb181 100755
--- a/indoteknik_custom/models/product_public_category.py
+++ b/indoteknik_custom/models/product_public_category.py
@@ -15,6 +15,7 @@ class ProductPublicCategory(models.Model):
child_frontend_id = fields.One2many('product.public.category', 'parent_frontend_id', string='Children Frontend Categories')
child_frontend_id2 = fields.Many2many('product.public.category', relation='website_categories_child_frontend_rel', column1='website_categories_homepage_id', column2='product_public_category_id', string='Category Level 3')
sequence_frontend = fields.Integer(help="Gives the sequence order when displaying a list of product categories.", index=True)
+ short_desc = fields.Char(string='Short Description')
# @api.model
# def _onchange_child_frontend_id2(self, parent):
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index 75adb352..397bd06d 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -18,13 +18,13 @@ class ProductTemplate(models.Model):
x_attribute_set_id = fields.Integer(
string="Magento Attribute Set ID",
- default=0,
- index=True,
- help="Attribute Set ID dari Magento"
+ help="Attribute Set ID dari Magento",
+ readonly=True
)
x_attribute_set_name = fields.Char(
string="Magento Attribute Set Name",
- help="Attribute Set Name dari Magento"
+ help="Attribute Set Name dari Magento",
+ readonly=True
)
image_carousel_lines = fields.One2many(
@@ -85,7 +85,6 @@ class ProductTemplate(models.Model):
merchandise_ok = fields.Boolean(string='Product Promotion')
print_barcode = fields.Boolean(string='Print Barcode', default=True)
# qr_code = fields.Binary("QR Code", compute='_compute_qr_code')
- # has_magento = fields.Boolean(string='Has Magento?', default=False)
@api.model
def create(self, vals):
@@ -967,6 +966,8 @@ class ProductProduct(models.Model):
return True
+ has_magento = fields.Boolean(string='Has Magento?', default=False, readonly=True)
+
def generate_product_sla(self):
product_variant_ids = self.env.context.get('active_ids', [])
product_variant = self.search([('id', 'in', product_variant_ids)])
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 3d22b0f0..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')
@@ -1081,18 +1082,11 @@ class PurchaseOrder(models.Model):
def button_confirm(self):
if self.env.user.id != 7 and not self.env.user.is_leader: # Pimpinan
if '/PJ/' in self.name:
- low_margin_lines = self.order_sales_match_line.filtered(
- lambda match: match.so_header_margin <= 15.0
- )
price_change_detected = any(line.price_unit_before for line in self.order_line)
- if low_margin_lines and price_change_detected:
- # raise UserError("Matches SO terdapat item dengan header margin SO <= 15%. Approval Pimpinan diperlukan.")
- raise UserError("Approval Pimpinan diperlukan jika terdapat perubahan Unit Price pada PO Line yang Matches SO item memiliki header margin SO <= 15%")
- # else:
- # is_po_manual = '/A/' not in self.name and '/MO/' not in self.name
- # if is_po_manual:
- # if not self.order_sales_match_line:
- # raise UserError("Tidak ada matches SO, Approval Pimpinan diperlukan.")
+ if price_change_detected:
+ if self.order_sales_match_line:
+ if self.total_percent_margin <= 15.0:
+ raise UserError("Approval Pimpinan diperlukan jika terdapat perubahan Unit Price pada PO Line dan Memiliki Margin <= 15%")
self._check_assets_note()
# self._check_payment_term() # check payment term
@@ -1417,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),
@@ -1466,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/purchasing_job.py b/indoteknik_custom/models/purchasing_job.py
index 7a7e70b4..e457b86c 100644
--- a/indoteknik_custom/models/purchasing_job.py
+++ b/indoteknik_custom/models/purchasing_job.py
@@ -24,7 +24,8 @@ class PurchasingJob(models.Model):
('apo', 'APO')
], string='APO?')
purchase_representative_id = fields.Many2one('res.users', string="Purchase Representative", readonly=True)
- note = fields.Char(string="Note Detail")
+ note = fields.Char(string="Note")
+ note_detail = fields.Text(string="Note Detail")
date_po = fields.Datetime(string='Date PO', copy=False)
so_number = fields.Text(string='SO Number', copy=False)
check_pj = fields.Boolean(compute='_get_check_pj', string='Linked')
@@ -126,6 +127,7 @@ class PurchasingJob(models.Model):
pmp.action,
max(pjs.status_apo::text) AS status_apo,
max(pjs.note::text) AS note,
+ max(pjs.note_detail::text) AS note_detail,
max(pjs.date_po::text) AS date_po,
pmp.so_number,
CASE
diff --git a/indoteknik_custom/models/purchasing_job_multi_update.py b/indoteknik_custom/models/purchasing_job_multi_update.py
index 80a43e45..0eca499f 100644
--- a/indoteknik_custom/models/purchasing_job_multi_update.py
+++ b/indoteknik_custom/models/purchasing_job_multi_update.py
@@ -20,10 +20,19 @@ class PurchasingJobMultiUpdate(models.TransientModel):
# purchasing_job_state.unlink()
- purchasing_job_state.create({
- 'purchasing_job_id': product.id,
- 'status_apo': 'apo',
- })
+ vals = {
+ 'purchasing_job_id': product.id, # Pastikan ID ini benar mapping ke ID produk
+ 'status_apo': 'apo',
+ }
+
+ if purchasing_job_state:
+ purchasing_job_state.write(vals)
+ else:
+ purchasing_job_state.create(vals)
+ # purchasing_job_state.create({
+ # 'purchasing_job_id': product.id,
+ # 'status_apo': 'apo',
+ # })
apo = self.env['automatic.purchase'].generate_regular_purchase(products)
return {
diff --git a/indoteknik_custom/models/purchasing_job_state.py b/indoteknik_custom/models/purchasing_job_state.py
index d014edfe..ca557de1 100644
--- a/indoteknik_custom/models/purchasing_job_state.py
+++ b/indoteknik_custom/models/purchasing_job_state.py
@@ -14,5 +14,6 @@ class PurchasingJobState(models.Model):
('not_apo', 'Belum APO'),
('apo', 'APO')
], string='APO?', copy=False)
- note = fields.Char(string="Note Detail", copy=False)
+ note = fields.Char(string="Note", copy=False)
+ note_detail = fields.Text(string="Note Detail", copy=False)
date_po = fields.Datetime(string='Date PO', copy=False)
diff --git a/indoteknik_custom/models/refund_sale_order.py b/indoteknik_custom/models/refund_sale_order.py
index c22e84ab..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.state != 'cancel'
+ 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']:
@@ -296,6 +297,7 @@ class RefundSaleOrder(models.Model):
])
piutangbca = self.env['account.move']
piutangmdr = self.env['account.move']
+ cabinvoice = self.env['account.move']
for inv_name in invoices.mapped('name'):
piutangbca |= self.env['account.move'].search([
@@ -308,6 +310,11 @@ class RefundSaleOrder(models.Model):
('journal_id', '=', 7),
('state', '=', 'posted'),
])
+ cabinvoice |= self.env['account.move'].search([
+ ('ref', 'ilike', inv_name),
+ ('journal_id', '=', 11),
+ ('state', '=', 'posted'),
+ ])
misc = self.env['account.move']
if invoices:
@@ -378,6 +385,7 @@ class RefundSaleOrder(models.Model):
has_moves3 = bool(moves3)
has_piutangmdr = bool(piutangmdr)
has_piutangbca = bool(piutangbca)
+ has_cabinvoice = bool(cabinvoice)
has_misc = bool(misc)
has_ongkir = bool(moves_ongkir)
ssos = self.env['sale.order'].browse(so_ids)
@@ -385,7 +393,7 @@ class RefundSaleOrder(models.Model):
sisa_uang_masuk = 0.0
- has_journal = has_moves or has_moves2 or has_moves3 or has_piutangbca or has_piutangmdr or has_misc
+ has_journal = has_moves or has_moves2 or has_moves3 or has_piutangbca or has_piutangmdr or has_misc or has_cabinvoice
if has_moves:
sisa_uang_masuk += sum(moves.mapped('amount_total_signed'))
@@ -397,6 +405,8 @@ class RefundSaleOrder(models.Model):
sisa_uang_masuk += sum(moves3.mapped('amount_total_signed'))
if has_piutangbca:
sisa_uang_masuk += sum(piutangbca.mapped('amount_total_signed'))
+ if has_cabinvoice:
+ sisa_uang_masuk += sum(cabinvoice.mapped('amount_total_signed'))
if has_piutangmdr:
sisa_uang_masuk += sum(piutangmdr.mapped('amount_total_signed'))
if has_misc:
@@ -425,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')
@@ -483,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.state != 'cancel'
+ 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)
@@ -526,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']:
@@ -539,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(
@@ -592,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),
@@ -602,6 +634,7 @@ class RefundSaleOrder(models.Model):
piutangbca = self.env['account.move']
piutangmdr = self.env['account.move']
+ cabinvoice = self.env['account.move']
for inv_name in invoice_ids.mapped('name'):
piutangbca |= self.env['account.move'].search([
@@ -614,6 +647,11 @@ class RefundSaleOrder(models.Model):
('journal_id', '=', 7),
('state', '=', 'posted'),
])
+ cabinvoice |= self.env['account.move'].search([
+ ('ref', 'ilike', inv_name),
+ ('journal_id', '=', 11),
+ ('state', '=', 'posted'),
+ ])
moves2 = self.env['account.move']
if rec.sale_order_ids:
@@ -659,15 +697,13 @@ class RefundSaleOrder(models.Model):
('line_ids.account_id', '=', 450),
('line_ids.account_id', '=', 668),
]
-
- if so_names:
domain += ['|'] * (len(so_names) - 1)
for name in so_names:
domain.append(('ref', 'ilike', name))
- moves_ongkir = self.env['account.move'].search(domain)
+ moves_ongkir = self.env['account.move'].search(domain)
- all_moves = moves | piutangbca | piutangmdr | misc | moves2 | moves_ongkir
+ all_moves = moves | piutangbca | piutangmdr | misc | moves2 | moves_ongkir | cabinvoice
for move in all_moves:
url = f"/web#id={move.id}&model=account.move&view_type=form"
@@ -695,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.state != 'cancel'
+ 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'))
@@ -713,6 +749,7 @@ class RefundSaleOrder(models.Model):
piutangbca = self.env['account.move']
piutangmdr = self.env['account.move']
+ cabinvoice = self.env['account.move']
for inv_name in all_invoices.mapped('name'):
piutangbca |= self.env['account.move'].search([
@@ -725,6 +762,11 @@ class RefundSaleOrder(models.Model):
('journal_id', '=', 7),
('state', '=', 'posted'),
])
+ cabinvoice |= self.env['account.move'].search([
+ ('ref', 'ilike', inv_name),
+ ('journal_id', '=', 11),
+ ('state', '=', 'posted'),
+ ])
misc = self.env['account.move']
if all_invoices:
@@ -799,6 +841,7 @@ class RefundSaleOrder(models.Model):
has_moves3 = bool(moves3)
has_piutangmdr = bool(piutangmdr)
has_piutangbca = bool(piutangbca)
+ has_cabinvoice = bool(cabinvoice)
has_misc = bool(misc)
has_ongkir = bool(moves_ongkir)
ssos = self.env['sale.order'].browse(so_ids)
@@ -812,6 +855,8 @@ class RefundSaleOrder(models.Model):
sisa_uang_masuk += sum(moves.mapped('amount_total_signed'))
if has_moves2:
sisa_uang_masuk += sum(moves2.mapped('amount_total_signed'))
+ if has_cabinvoice:
+ sisa_uang_masuk += sum(cabinvoice.mapped('amount_total_signed'))
if has_moves3:
sisa_uang_masuk += sum(moves3.mapped('amount_total_signed'))
if has_piutangbca:
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 17691798..90cd5fa2 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -647,7 +647,7 @@ class SaleOrder(models.Model):
def _get_biteship_courier_codes(self):
return [
- 'gojek','grab','deliveree','lalamove','jne','tiki','ninja','lion','rara','sicepat','jnt','pos','idexpress','rpx','wahana','jdl','pos','anteraja','sap','paxel','borzo'
+ 'gojek','grab','deliveree','lalamove','jne','ninja','lion','rara','sicepat','jnt','pos','idexpress','rpx','wahana','jdl','anteraja','sap','paxel','borzo'
]
@api.onchange('carrier_id')
@@ -2305,7 +2305,7 @@ class SaleOrder(models.Model):
search_bom = self.env['mrp.production'].search([('product_id', '=', line.product_id.id), ('sale_order', '=', order.id), ('state', '!=', 'cancel')],
order='name desc')
if search_bom:
- confirmed_bom = search_bom.filtered(lambda x: x.state == 'confirmed' or x.state == 'done')
+ confirmed_bom = search_bom.filtered(lambda x: x.state in ['confirmed', 'to_close', 'progress', 'done'])
if not confirmed_bom:
raise UserError(
"Product BOM belum dikonfirmasi di Manufacturing Orders. Silakan hubungi Purchasing.")
@@ -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.state != 'cancel')
+ invoice_ids = self.invoice_ids.filtered(lambda inv: inv.state == 'posted')
moves = self.env['account.move'].search([
('sale_id', '=', self.id),
@@ -3447,6 +3461,7 @@ class SaleOrder(models.Model):
])
piutangbca = self.env['account.move']
piutangmdr = self.env['account.move']
+ cabinvoice = self.env['account.move']
for inv_name in invoice_ids.mapped('name'):
piutangbca |= self.env['account.move'].search([
@@ -3459,6 +3474,11 @@ class SaleOrder(models.Model):
('journal_id', '=', 7),
('state', '=', 'posted'),
])
+ cabinvoice |= self.env['account.move'].search([
+ ('ref', 'ilike', inv_name),
+ ('journal_id', '=', 11),
+ ('state', '=', 'posted'),
+ ])
moves2 = self.env['account.move'].search([
('ref', 'ilike', self.name),
@@ -3473,6 +3493,7 @@ class SaleOrder(models.Model):
has_moves2 = bool(moves2)
has_piutangmdr = bool(piutangmdr)
has_piutangbca = bool(piutangbca)
+ has_cabinvoice = bool(cabinvoice)
has_settlement = self.payment_status == 'settlement'
if has_moves and has_settlement:
@@ -3483,6 +3504,8 @@ class SaleOrder(models.Model):
total_uang_muka = sum(moves2.mapped('amount_total_signed'))
elif has_settlement:
total_uang_muka = self.gross_amount
+ elif has_cabinvoice:
+ total_uang_muka = sum(cabinvoice.mapped('amount_total_signed'))
elif has_piutangbca:
total_uang_muka = sum(piutangbca.mapped('amount_total_signed'))
elif has_piutangmdr:
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..5f5f2d79 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.Text(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/solr/apache_solr.py b/indoteknik_custom/models/solr/apache_solr.py
index c2283421..21fb15d2 100644
--- a/indoteknik_custom/models/solr/apache_solr.py
+++ b/indoteknik_custom/models/solr/apache_solr.py
@@ -7,12 +7,12 @@ import time
from odoo.tools.config import config
_logger = logging.getLogger(__name__)
-# _solr = pysolr.Solr('http://10.148.0.5:8983/solr/product/', always_commit=True, timeout=30)
-# _variants_solr = pysolr.Solr('http://10.148.0.5:8983/solr/variants/', always_commit=True, timeout=30)
-# _recommendation_solr = pysolr.Solr('http://10.148.0.5:8983/solr/recommendation/', always_commit=True, timeout=30)
-_solr = pysolr.Solr('http://localhost:8983/solr/product/', always_commit=True, timeout=30)
-_variants_solr = pysolr.Solr('http://localhost:8983/solr/variants/', always_commit=True, timeout=30)
-_recommendation_solr = pysolr.Solr('http://localhost:8983/solr/recommendation/', always_commit=True, timeout=30)
+_solr = pysolr.Solr('http://10.148.0.5:8983/solr/product/', always_commit=True, timeout=30)
+_variants_solr = pysolr.Solr('http://10.148.0.5:8983/solr/variants/', always_commit=True, timeout=30)
+_recommendation_solr = pysolr.Solr('http://10.148.0.5:8983/solr/recommendation/', always_commit=True, timeout=30)
+# _solr = pysolr.Solr('http://35.219.88.139/solr/product/', always_commit=True, timeout=30)
+# _variants_solr = pysolr.Solr('http://35.219.88.139/solr/variants/', always_commit=True, timeout=30)
+# _recommendation_solr = pysolr.Solr('http://35.219.88.139/solr/recommendation/', always_commit=True, timeout=30)
# _solr = pysolr.Solr('http://34.101.189.218:8983/solr/product/', always_commit=True, timeout=30) # for development only
@@ -27,7 +27,7 @@ class ApacheSolr(models.Model):
if env == 'development':
url = 'http://localhost:8983/solr/'
elif env == 'production':
- url = 'http://locahost:8983/solr/'
+ url = 'http://34.101.189.218:8983/solr/'
return pysolr.Solr(url + schema, always_commit=False, timeout=10)
@@ -258,6 +258,7 @@ class ApacheSolr(models.Model):
'id': variant.id,
'display_name_s': variant.display_name,
'name_s': variant.name,
+ 'search_keywords_t': variant.display_name,
'default_code_s': variant.default_code or '',
'product_rating_f': variant.product_tmpl_id.virtual_rating,
'product_id_i': variant.id,
@@ -269,6 +270,7 @@ class ApacheSolr(models.Model):
'tax_f': tax,
'stock_total_f': variant.qty_stock_vendor,
'weight_f': variant.product_tmpl_id.weight,
+ 'has_magento_b': variant.has_magento,
'attribute_set_id_i': variant.product_tmpl_id.x_attribute_set_id or 0,
'attribute_set_name_s': variant.product_tmpl_id.x_attribute_set_name or '',
'manufacture_id_i': variant.product_tmpl_id.x_manufacture.id or 0,
diff --git a/indoteknik_custom/models/solr/product_product.py b/indoteknik_custom/models/solr/product_product.py
index 7df56e57..a90bcb98 100644
--- a/indoteknik_custom/models/solr/product_product.py
+++ b/indoteknik_custom/models/solr/product_product.py
@@ -64,6 +64,7 @@ class ProductProduct(models.Model):
'id': variant.id,
'display_name_s': variant.display_name,
'name_s': variant.name,
+ 'search_keywords_t': variant.display_name,
'default_code_s': variant.default_code or '',
'product_rating_f': variant.product_tmpl_id.virtual_rating,
'product_id_i': variant.id,
@@ -73,6 +74,7 @@ class ProductProduct(models.Model):
'image_mobile_s': ir_attachment.api_image('product.template', 'image_256', variant.product_tmpl_id.id),
'stock_total_f': variant.qty_free_bandengan,
'weight_f': variant.weight,
+ 'has_magento_b': variant.has_magento,
'attribute_set_id_i': variant.product_tmpl_id.x_attribute_set_id or 0,
'attribute_set_name_s': variant.product_tmpl_id.x_attribute_set_name or '',
'manufacture_id_i': variant.product_tmpl_id.x_manufacture.id or 0,
diff --git a/indoteknik_custom/models/stock_inventory.py b/indoteknik_custom/models/stock_inventory.py
index 84eb5a17..efd52e5c 100644
--- a/indoteknik_custom/models/stock_inventory.py
+++ b/indoteknik_custom/models/stock_inventory.py
@@ -17,6 +17,35 @@ class StockInventory(models.Model):
('out', 'Adjusment Out'),
], string='Adjusments Type', required=True)
+ approval_state = fields.Selection([
+ ('logistic', 'Logistic'),
+ ('accounting', 'Accounting'),
+ ('approved', 'Approved'),
+ ], tracking=True, readonly=True)
+
+ def action_validate(self):
+ if self.adjusment_type == 'out':
+
+ if self.approval_state != 'approved':
+
+ if self.approval_state == 'logistic':
+ if not self.env.user.has_group('indoteknik_custom.group_role_logistic'):
+ raise UserError("Adjustment Out harus dilakukan oleh Logistic")
+ self.approval_state = 'accounting'
+ return True
+
+ elif self.approval_state == 'accounting':
+ if not self.env.user.has_group('indoteknik_custom.group_role_fat'):
+ raise UserError("Adjustment Out harus dilakukan oleh Accounting")
+ self.approval_state = 'approved'
+ return super(StockInventory, self).action_validate()
+
+ else:
+ raise UserError("Adjustment Out harus melalui approval terlebih dahulu.")
+
+ return super(StockInventory, self).action_validate()
+
+
def _generate_number_stock_inventory(self):
"""Men-generate nomor untuk semua stock inventory yang belum memiliki number."""
stock_records = self.env['stock.inventory'].search([('number', '=', False)], order='id asc')
@@ -53,13 +82,19 @@ 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.env.user.id not in [21, 17, 571, 28, 25]:
+ raise UserError("Hanya Rafly, Denise, Iqmal, dan Stephan yang bisa start inventory")
return super(StockInventory, self).action_start()
@api.model
def create(self, vals):
"""Pastikan nomor hanya dibuat saat penyimpanan."""
+
+ if vals.get('adjusment_type') == 'in':
+ vals['approval_state'] = False
+ elif vals.get('adjusment_type') == 'out':
+ vals['approval_state'] = 'logistic'
+
if 'adjusment_type' in vals and not vals.get('number'):
vals['number'] = False # Jangan buat number otomatis dulu
@@ -69,12 +104,17 @@ class StockInventory(models.Model):
self._assign_number(order) # Generate number setelah save
return order
+
def write(self, vals):
"""Jika adjusment_type diubah, generate ulang nomor."""
res = super(StockInventory, self).write(vals)
if 'adjusment_type' in vals:
for record in self:
+ if record.adjusment_type == 'in':
+ record.approval_state = False
+ elif record.adjusment_type == 'out' and record.approval_state == False:
+ record.approval_state = 'logistic'
self._assign_number(record)
return res
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 b4b61296..0c3bfa3b 100755
--- a/indoteknik_custom/models/x_manufactures.py
+++ b/indoteknik_custom/models/x_manufactures.py
@@ -50,6 +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', default=0)
def _compute_vendor_ids(self):
for manufacture in self: