summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-06-22 19:14:06 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-06-22 19:14:06 +0700
commit4430c79a3388c591725cdbd12e32c6c371b6ecd3 (patch)
treea5935e7e5fda73c7929d4ef5a84e734b9c305626
parent680748ae128a90b9999acff60c770e2472c7fcbe (diff)
parenteeb72c4ed24c33403bb733a51198b9cc0f356e6a (diff)
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into tukar_guling
-rw-r--r--indoteknik_api/controllers/api_v1/partner.py2
-rw-r--r--indoteknik_api/controllers/api_v1/state.py10
-rw-r--r--indoteknik_custom/models/account_move.py40
-rw-r--r--indoteknik_custom/models/commision.py14
-rw-r--r--indoteknik_custom/models/invoice_reklas.py10
-rwxr-xr-xindoteknik_custom/models/purchase_order.py75
-rwxr-xr-xindoteknik_custom/models/sale_order.py57
-rw-r--r--indoteknik_custom/models/sale_orders_multi_update.py7
-rw-r--r--indoteknik_custom/models/user_pengajuan_tempo_request.py6
-rw-r--r--indoteknik_custom/views/account_move.xml6
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml9
-rwxr-xr-xindoteknik_custom/views/sale_order.xml15
-rw-r--r--indoteknik_custom/views/stock_picking.xml1
13 files changed, 218 insertions, 34 deletions
diff --git a/indoteknik_api/controllers/api_v1/partner.py b/indoteknik_api/controllers/api_v1/partner.py
index 37ae8d5f..b8bd21be 100644
--- a/indoteknik_api/controllers/api_v1/partner.py
+++ b/indoteknik_api/controllers/api_v1/partner.py
@@ -322,7 +322,7 @@ class Partner(controller.Controller):
data = True if pengajuan_tempo.id else False
return self.response(data)
- @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST'], csrf=False)
+ @http.route(prefix + 'partner/pengajuan_tempo', auth='public', methods=['POST', 'OPTIONS'], csrf=False)
@controller.Controller.must_authorized()
def write_pengajuan_tempo(self, **kw):
try:
diff --git a/indoteknik_api/controllers/api_v1/state.py b/indoteknik_api/controllers/api_v1/state.py
index 958359a7..c03042e7 100644
--- a/indoteknik_api/controllers/api_v1/state.py
+++ b/indoteknik_api/controllers/api_v1/state.py
@@ -8,12 +8,8 @@ class District(controller.Controller):
@http.route(prefix + 'state', auth='public', methods=['GET', 'OPTIONS'])
@controller.Controller.must_authorized()
def get_state(self, **kw):
- tempo = kw.get('tempo')
- parameters = []
- if tempo == 'true':
- parameters.append(('country_id', '=', 100))
+ parameters = [('country_id', '=', 100)] # selalu ambil country_id = 100
-
name = kw.get('name')
if name:
name = '%' + name.replace(' ', '%') + '%'
@@ -22,7 +18,7 @@ class District(controller.Controller):
states = request.env['res.country.state'].search(parameters)
data = []
for state in states:
- data.append({ 'id': state.id, 'name': state.name})
-
+ data.append({'id': state.id, 'name': state.name})
+
return self.response(data)
diff --git a/indoteknik_custom/models/account_move.py b/indoteknik_custom/models/account_move.py
index 54eaabcf..66020a69 100644
--- a/indoteknik_custom/models/account_move.py
+++ b/indoteknik_custom/models/account_move.py
@@ -87,6 +87,17 @@ class AccountMove(models.Model):
# result.append((move.id, move.display_name))
# return result
+ @api.onchange('invoice_date')
+ def _onchange_invoice_date(self):
+ if self.invoice_date:
+ self.date = self.invoice_date
+
+ @api.onchange('date')
+ def _onchange_date(self):
+ if self.date:
+ self.invoice_date = self.date
+
+
def compute_length_of_payment(self):
for rec in self:
payment_term = rec.invoice_payment_term_id.line_ids[0].days
@@ -145,13 +156,38 @@ class AccountMove(models.Model):
}
template.send_mail(record.id, email_values=email_values, force_send=True)
+ # @api.model
+ # def create(self, vals):
+ # vals['nomor_kwitansi'] = self.env['ir.sequence'].next_by_code('nomor.kwitansi') or '0'
+ # result = super(AccountMove, self).create(vals)
+ # # result._update_line_name_from_ref()
+ # return result
+
@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)
- # result._update_line_name_from_ref()
+
+ # Tambahan: jika ini Vendor Bill dan tanggal belum diisi
+ if result.move_type == 'in_invoice' and not vals.get('invoice_date') and not vals.get('date'):
+ po = result.purchase_order_id
+ if po:
+ # Cari receipt dari PO
+ picking = self.env['stock.picking'].search([
+ ('purchase_id', '=', po.id),
+ ('picking_type_code', '=', 'incoming'),
+ ('state', '=', 'done'),
+ ('date_done', '!=', False),
+ ], order='date_done desc', limit=1)
+
+ if picking:
+ receipt_date = picking.date_done
+ result.invoice_date = receipt_date
+ result.date = receipt_date
+
return result
+
def compute_so_shipping_paid_by(self):
for record in self:
record.so_shipping_paid_by = record.sale_id.shipping_paid_by
diff --git a/indoteknik_custom/models/commision.py b/indoteknik_custom/models/commision.py
index d3392a0c..03d32d2d 100644
--- a/indoteknik_custom/models/commision.py
+++ b/indoteknik_custom/models/commision.py
@@ -177,8 +177,9 @@ class CustomerCommision(models.Model):
], string='Status')
commision_percent = fields.Float(string='Commision %', tracking=3)
commision_amt = fields.Float(string='Commision Amount', tracking=3)
- cashback = fields.Float(string='Cashback', tracking=3)
- total_commision = fields.Float(string='Total Commision', tracking=3)
+ cashback = fields.Float(string='Cashback', compute="compute_cashback")
+ total_commision = fields.Float(string='Total Commision', compute="compute_cashback")
+ total_cashback = fields.Float(string='Total Cashback')
commision_amt_text = fields.Char(string='Commision Amount Text', compute='compute_delivery_amt_text')
total_dpp = fields.Float(string='Total DPP', compute='_compute_total_dpp')
commision_type = fields.Selection([
@@ -318,9 +319,6 @@ class CustomerCommision(models.Model):
if self.commision_amt == 0:
self.commision_amt = self.commision_percent * self.total_dpp // 100
- if self.commision_type == 'cashback':
- self.cashback = self.commision_amt * 0.15
- self.total_commision = self.commision_amt * 0.85
@api.constrains('commision_amt')
def _onchange_commision_amt(self):
@@ -333,11 +331,13 @@ class CustomerCommision(models.Model):
if self.total_dpp > 0 and self.commision_percent == 0:
self.commision_percent = (self.commision_amt / self.total_dpp) * 100
- @api.constrains('commision_type')
- def _onchange_commision_amt(self):
+ def compute_cashback(self):
if self.commision_type == 'cashback' and self.commision_amt > 0:
self.cashback = self.commision_amt * 0.15
self.total_commision = self.commision_amt * 0.85
+ else:
+ self.cashback = 0
+ self.total_commision = 0
def _compute_total_dpp(self):
for data in self:
diff --git a/indoteknik_custom/models/invoice_reklas.py b/indoteknik_custom/models/invoice_reklas.py
index 5145e098..4ed7f0bf 100644
--- a/indoteknik_custom/models/invoice_reklas.py
+++ b/indoteknik_custom/models/invoice_reklas.py
@@ -87,10 +87,10 @@ class InvoiceReklas(models.TransientModel):
po_name = invoice.purchase_order_id.name if invoice.purchase_order_id else ''
# Susun nama referensi dengan aman
- ref_name = 'REKLAS {} UANG MUKA {} {}{} {}'.format(
+ ref_name = 'REKLAS {} UANG MUKA {}{}{} {}'.format(
self.reklas_id.name or '',
'PENJUALAN' if self.reklas_type == 'penjualan' else 'PEMBELIAN',
- invoice.name or '',
+ f" {invoice.name}" if invoice.name != self.reklas_id.name else '',
f" - {po_name}" if po_name else '',
invoice.partner_id.name or ''
)
@@ -102,11 +102,15 @@ class InvoiceReklas(models.TransientModel):
'journal_id': 13
}
+ if invoice.purchase_order_id:
+ parameters_header['purchase_order_id'] = invoice.purchase_order_id.id
+
account_move = request.env['account.move'].create([parameters_header])
_logger.info('Success Reklas with %s' % account_move.name)
# ✅ Set Bill asal sebagai source document
- account_move.bill_id = invoice.id
+ if invoice.move_type == 'in_invoice':
+ account_move.bill_id = invoice.id
# Tambahkan info asal invoice ke jurnal (opsional)
account_move.invoice_origin = invoice.name
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 004a1fa4..1a7e50f8 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -91,6 +91,63 @@ class PurchaseOrder(models.Model):
is_cab_visible = fields.Boolean(string='Tampilkan Tombol CAB', compute='_compute_is_cab_visible')
+ # picking_ids = fields.One2many('stock.picking', 'purchase_id', string='Pickings')
+
+ bu_related_count = fields.Integer(
+ string="BU Related Count",
+ compute='_compute_bu_related_count'
+ )
+
+ @api.depends('name')
+ def _compute_bu_related_count(self):
+ for order in self:
+ if not order.name:
+ order.bu_related_count = 0
+ continue
+
+ # BU langsung dari PO
+ base_bu = self.env['stock.picking'].search([
+ ('name', 'ilike', 'BU/'),
+ ('origin', 'ilike', order.name)
+ ])
+ base_names = base_bu.mapped('name')
+
+ # Return dari BU di atas
+ return_bu = self.env['stock.picking'].search([
+ ('origin', 'in', [f"Return of {name}" for name in base_names])
+ ])
+
+ order.bu_related_count = len(base_bu) + len(return_bu)
+
+ def action_view_related_bu(self):
+ self.ensure_one()
+
+ # Step 1: cari semua BU pertama (PUT, INT) yang berasal dari PO ini
+ base_bu = self.env['stock.picking'].search([
+ ('name', 'ilike', 'BU/'),
+ ('origin', 'ilike', self.name)
+ ])
+ base_bu_names = base_bu.mapped('name')
+
+ # Step 2: cari BU turunan (seperti BU/VRT) yang origin-nya mengandung nama BU tersebut
+ domain = [
+ '|',
+ '&',
+ ('name', 'ilike', 'BU/'),
+ ('origin', 'ilike', self.name),
+ ('origin', 'in', [f"Return of {name}" for name in base_bu_names])
+ ]
+
+ return {
+ 'name': 'Related BU (INT/PRT/PUT/VRT)',
+ 'type': 'ir.actions.act_window',
+ 'res_model': 'stock.picking',
+ 'view_mode': 'tree,form',
+ 'target': 'current',
+ 'domain': domain,
+ }
+
+
@api.depends('move_id.state')
def _compute_is_cab_visible(self):
for order in self:
@@ -452,6 +509,18 @@ class PurchaseOrder(models.Model):
'company_id': self.company_id.id,
'payment_schedule': payment_schedule
}
+
+ receipt = self.env['stock.picking'].search([
+ ('purchase_id', '=', self.id),
+ ('state', '=', 'done'),
+ ('picking_type_code', '=', 'incoming'),
+ ('date_done', '!=', False)
+ ], order='date_done desc', limit=1)
+
+ if receipt:
+ invoice_vals['invoice_date'] = receipt.date_done
+ invoice_vals['date'] = receipt.date_done
+
return invoice_vals
def _compute_matches_so(self):
@@ -917,6 +986,12 @@ class PurchaseOrder(models.Model):
if self.product_bom_id:
self._remove_product_bom()
+ # Tambahan: redirect ke BU hanya untuk single PO yang berhasil dikonfirmasi
+ _logger.info("Jumlah PO: %s | State: %s", len(self), self.state)
+ if len(self) == 1:
+ _logger.info("Redirecting ke BU")
+ return self.action_view_related_bu()
+
return res
def _remove_product_bom(self):
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 68beffbc..c8d4a712 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -349,6 +349,47 @@ class SaleOrder(models.Model):
date_unhold = fields.Datetime(string='Date Unhold', tracking=True, readonly=True, help='Waktu ketika SO di Unhold'
)
+ et_products = fields.Datetime(string='ET Products', compute='_compute_et_products', help="Leadtime produk berdasarkan SLA vendor, tanpa logistik.")
+
+ eta_date_reserved = fields.Datetime(
+ string="Date Reserved",
+ compute="_compute_eta_date_reserved",
+ help="Tanggal pertama kali barang berhasil di-reservasi pada DO (BU/PICK/) yang berstatus Siap Dikirim."
+ )
+
+ @api.depends('order_line.product_id', 'date_order')
+ def _compute_et_products(self):
+ jakarta = pytz.timezone("Asia/Jakarta")
+ for order in self:
+ if not order.order_line or not order.date_order:
+ order.et_products = False
+ continue
+
+ # Ambil tanggal order sebagai basis
+ base_date = order.date_order
+ if base_date.tzinfo is None:
+ base_date = jakarta.localize(base_date)
+ else:
+ base_date = base_date.astimezone(jakarta)
+
+ # Ambil nilai SLA vendor dalam hari
+ sla_data = order.calculate_sla_by_vendor(order.order_line)
+ sla_days = sla_data.get('slatime', 1)
+
+ # Hitung ETA produk (tanpa logistik)
+ eta_datetime = base_date + timedelta(days=sla_days)
+
+ # Simpan ke field sebagai UTC-naive datetime (standar Odoo)
+ order.et_products = eta_datetime.astimezone(pytz.utc).replace(tzinfo=None)
+
+ @api.depends('picking_ids.state', 'picking_ids.date_reserved')
+ def _compute_eta_date_reserved(self):
+ for order in self:
+ pickings = order.picking_ids.filtered(
+ lambda p: p.state == 'assigned' and p.date_reserved and 'BU/PICK/' in (p.name or '')
+ )
+ order.eta_date_reserved = min(pickings.mapped('date_reserved')) if pickings else False
+
@api.onchange('shipping_cost_covered')
def _onchange_shipping_cost_covered(self):
if self.shipping_cost_covered == 'indoteknik' and self.select_shipping_option == 'biteship':
@@ -486,7 +527,7 @@ class SaleOrder(models.Model):
('sale_order_id', '=', sale_order_id),
('provider', 'ilike', provider),
])
- if matched == 0:
+ if self.select_shipping_option == 'biteship' and matched == 0:
self.carrier_id = self._origin.carrier_id
self.shipping_option_id = self._origin.shipping_option_id or False
return {
@@ -550,11 +591,11 @@ class SaleOrder(models.Model):
if shipping_option.exists():
courier_service = shipping_option.courier_service_code
vals['delivery_service_type'] = courier_service
- _logger.info("đŸ›°ī¸ Set delivery_service_type: %s from shipping_option_id: %s", courier_service, shipping_option_id)
+ _logger.info("Set delivery_service_type: %s from shipping_option_id: %s", courier_service, shipping_option_id)
else:
- _logger.warning("âš ī¸ shipping_option_id %s not found or invalid.", shipping_option_id)
+ _logger.warning("shipping_option_id %s not found or invalid.", shipping_option_id)
else:
- _logger.info("â„šī¸ shipping_option_id not found in vals or record.")
+ _logger.info("shipping_option_id not found in vals or record.")
# @api.model
# def fields_get(self, allfields=None, attributes=None):
@@ -1071,7 +1112,13 @@ class SaleOrder(models.Model):
message_lines.append("<br/>")
origin_address = "Jl. Bandengan Utara Komp A & BRT. Penjaringan, Kec. Penjaringan, Jakarta (BELAKANG INDOMARET) KOTA JAKARTA UTARA PENJARINGAN"
- destination_address = shipping_address.alamat_lengkap_text or shipping_address.street or shipping_address.name or ''
+ destination_address = ', '.join(filter(None, [
+ shipping_address.street,
+ shipping_address.kelurahan_id.name if shipping_address.kelurahan_id else None,
+ shipping_address.kecamatan_id.name if shipping_address.kecamatan_id else None,
+ shipping_address.kota_id.name if shipping_address.kota_id else None,
+ shipping_address.state_id.name if shipping_address.state_id else None
+ ]))
if use_coordinate:
origin_suffix = f"(Koordinat: {origin_data.get('origin_latitude')}, {origin_data.get('origin_longitude')})"
destination_suffix = f"(Koordinat: {destination_data.get('destination_latitude')}, {destination_data.get('destination_longitude')})"
diff --git a/indoteknik_custom/models/sale_orders_multi_update.py b/indoteknik_custom/models/sale_orders_multi_update.py
index 95cfde21..962f60b5 100644
--- a/indoteknik_custom/models/sale_orders_multi_update.py
+++ b/indoteknik_custom/models/sale_orders_multi_update.py
@@ -11,6 +11,13 @@ class SaleOrdersMultiUpdate(models.TransientModel):
sale_ids = self._context['sale_ids']
sales = self.env['sale.order'].browse(sale_ids)
sales.action_multi_update_invoice_status()
+
+ for sale in sales:
+ sale.message_post(
+ body="Sales Order has been marked as Completed",
+ message_type="comment"
+ )
+
return {
'type': 'ir.actions.client',
'tag': 'display_notification',
diff --git a/indoteknik_custom/models/user_pengajuan_tempo_request.py b/indoteknik_custom/models/user_pengajuan_tempo_request.py
index 87227764..600381c0 100644
--- a/indoteknik_custom/models/user_pengajuan_tempo_request.py
+++ b/indoteknik_custom/models/user_pengajuan_tempo_request.py
@@ -371,7 +371,7 @@ class UserPengajuanTempoRequest(models.Model):
@api.onchange('tempo_limit')
def _onchange_tempo_limit(self):
for tempo in self:
- if tempo.env.user.id not in (7, 688, 28, 377, 12182, 375):
+ if tempo.env.user.id not in (7, 688, 28, 19, 375):
raise UserError("Limit tempo hanya bisa diubah oleh Sales Manager atau Direktur")
def button_approve(self):
for tempo in self:
@@ -381,7 +381,7 @@ class UserPengajuanTempoRequest(models.Model):
if tempo.env.user.id in (688, 28, 7):
raise UserError("Pengajuan tempo harus di approve oleh sales manager terlebih dahulu")
else:
- if tempo.env.user.id not in (377, 12182, 375):
+ if tempo.env.user.id not in (375, 19):
# if tempo.env.user.id != 12182:
raise UserError("Pengajuan tempo hanya bisa di approve oleh sales manager")
else:
@@ -400,7 +400,7 @@ class UserPengajuanTempoRequest(models.Model):
if tempo.env.user.id == 7:
raise UserError("Pengajuan tempo harus di approve oleh Finence terlebih dahulu")
else:
- if tempo.env.user.id not in (688, 28, 12182):
+ if tempo.env.user.id not in (688, 28):
# if tempo.env.user.id not in (288,28,12182):
raise UserError("Pengajuan tempo hanya bisa di approve oleh Finence")
else:
diff --git a/indoteknik_custom/views/account_move.xml b/indoteknik_custom/views/account_move.xml
index 0c2f9a68..0fc62293 100644
--- a/indoteknik_custom/views/account_move.xml
+++ b/indoteknik_custom/views/account_move.xml
@@ -33,12 +33,12 @@
</field>
<field name="invoice_date" position="after">
<field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/>
- <field name="purchase_order_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'in_invoice')]}"/>
+ <!-- <field name="purchase_order_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'in_invoice')]}"/> -->
</field>
<field name="ref" position="after">
<field name="sale_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'entry')]}"/>
- <!-- <field name="purchase_order_id" context="{'form_view_ref': 'purchase.purchase_order_form'}" options="{'no_create': True}"/> -->
- <field name="bill_id" readonly="1" attrs="{'invisible': ['|', ('move_type', '!=', 'entry'), ('purchase_order_id', '!=', False)]}"/>
+ <field name="purchase_order_id" context="{'form_view_ref': 'purchase.purchase_order_form'}" options="{'no_create': True}"/>
+ <field name="bill_id" readonly="1"/>
</field>
<field name="partner_shipping_id" position="before">
<field name="real_invoice_id" readonly="1" attrs="{'invisible': [('move_type', '!=', 'out_invoice')]}"/>
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index 9084bcbb..7b568d07 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -27,6 +27,15 @@
<t t-esc="record.move_id.name"/>
</span>
</button>
+ <button type="object"
+ name="action_view_related_bu"
+ class="oe_stat_button"
+ icon="fa-truck"
+ style="width: 200px;"
+ attrs="{'invisible': [('state', 'in', ['draft', 'sent'])]}">
+ <field name="bu_related_count" widget="statinfo" string="BU Related"/>
+ </button>
+ <field name="picking_count" invisible="1"/>
</xpath>
<button id="draft_confirm" position="after">
<button name="po_approve"
diff --git a/indoteknik_custom/views/sale_order.xml b/indoteknik_custom/views/sale_order.xml
index d0442677..2a159307 100755
--- a/indoteknik_custom/views/sale_order.xml
+++ b/indoteknik_custom/views/sale_order.xml
@@ -103,10 +103,10 @@
<field name="compute_fullfillment" invisible="1" />
</field>
<field name="tag_ids" position="after">
- <field name="eta_date_start"/>
+ <!-- <field name="eta_date_start"/> -->
<t t-esc="' to '"/>
- <field name="eta_date" readonly="1"/>
- <field name="expected_ready_to_ship"/>
+ <!-- <field name="eta_date" readonly="1"/> -->
+ <!-- <field name="expected_ready_to_ship"/> -->
<field name="ready_to_ship_status_detail"/>
<field name="flash_sale"/>
<field name="margin_after_delivery_purchase"/>
@@ -144,6 +144,15 @@
<field name="date_doc_kirim" readonly="1"/>
<field name="notification" readonly="1"/>
</field>
+ <xpath expr="//page[@name='other_information']/group/group[@name='sale_reporting']" position="after">
+ <group string="ETA">
+ <field name="et_products"/>
+ <field name="eta_date_reserved"/>
+ <field name="expected_ready_to_ship"/>
+ <field name="eta_date_start"/>
+ <field name="eta_date" readonly="1"/>
+ </group>
+ </xpath>
<xpath expr="//form/sheet/notebook/page/field[@name='order_line']"
position="attributes">
<attribute name="attrs">
diff --git a/indoteknik_custom/views/stock_picking.xml b/indoteknik_custom/views/stock_picking.xml
index 97a9fbed..c088e00c 100644
--- a/indoteknik_custom/views/stock_picking.xml
+++ b/indoteknik_custom/views/stock_picking.xml
@@ -17,6 +17,7 @@
<field name="driver_arrival_date" optional="hide"/>
<field name="note_logistic" optional="hide"/>
<field name="note" optional="hide"/>
+ <field name="sj_return_date" optional="hide"/>
<field name="date_reserved" optional="hide"/>
<field name="state_reserve" optional="hide"/>
<field name="state_packing" widget="badge" decoration-success="state_packing == 'packing_done'" decoration-danger="state_packing == 'not_packing'" optional="hide"/>