summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiqdad <ahmadmiqdad27@gmail.com>2025-07-03 19:56:23 +0700
committerMiqdad <ahmadmiqdad27@gmail.com>2025-07-03 19:56:23 +0700
commit1991342a572fd6a3a4f9b2c988db939748125e4c (patch)
tree556759d4f7c098728c49b16958a2a940a6c55302
parentde50bcf16b21abf7b8e45fb59b366c594bc00038 (diff)
parentdb60e29b2f599ac21e96ffdfb5be94e3c0ba6a2f (diff)
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into tukar_guling
-rw-r--r--indoteknik_custom/models/coretax_fatur.py6
-rw-r--r--indoteknik_custom/models/mrp_production.py5
-rwxr-xr-xindoteknik_custom/models/purchase_order.py61
-rwxr-xr-xindoteknik_custom/models/sale_order.py153
-rw-r--r--indoteknik_custom/views/mrp_production.xml2
-rwxr-xr-xindoteknik_custom/views/purchase_order.xml3
-rw-r--r--indoteknik_custom/views/res_partner.xml11
7 files changed, 168 insertions, 73 deletions
diff --git a/indoteknik_custom/models/coretax_fatur.py b/indoteknik_custom/models/coretax_fatur.py
index 9b1544d3..ce94306f 100644
--- a/indoteknik_custom/models/coretax_fatur.py
+++ b/indoteknik_custom/models/coretax_fatur.py
@@ -3,6 +3,9 @@ import xml.etree.ElementTree as ET
from xml.dom import minidom
import base64
import re
+import logging
+
+_logger = logging.getLogger(__name__)
class CoretaxFaktur(models.Model):
@@ -72,8 +75,9 @@ class CoretaxFaktur(models.Model):
ET.SubElement(tax_invoice, 'BuyerEmail').text = invoice.partner_id.email or ''
ET.SubElement(tax_invoice, 'BuyerIDTKU').text = buyerIDTKU
+ _logger.info(" invoice down_payments: %s", invoice.down_payment)
# Handle product lines based on down_payments flag
- if down_payments and invoice.invoice_origin:
+ if invoice.down_payment and invoice.invoice_origin:
# Get from sale.order.line for down payment
sale_order = invoice.sale_id
if sale_order:
diff --git a/indoteknik_custom/models/mrp_production.py b/indoteknik_custom/models/mrp_production.py
index 14821f27..85b8405f 100644
--- a/indoteknik_custom/models/mrp_production.py
+++ b/indoteknik_custom/models/mrp_production.py
@@ -110,8 +110,9 @@ class MrpProduction(models.Model):
'picking_type_id': 28, # indoteknik bandengan receipts
'date_order': current_time,
'product_bom_id': self.product_id.id,
- # 'sale_order_id': self.sale_order_id.id,
- 'note_description': 'from Manufacturing Order'
+ 'sale_order_id': self.sale_order.id,
+ 'manufacturing_id': self.id,
+ 'note_description': 'from Manufacturing Order',
}
domain = [
diff --git a/indoteknik_custom/models/purchase_order.py b/indoteknik_custom/models/purchase_order.py
index 1a7e50f8..4dc26d74 100755
--- a/indoteknik_custom/models/purchase_order.py
+++ b/indoteknik_custom/models/purchase_order.py
@@ -97,46 +97,69 @@ class PurchaseOrder(models.Model):
string="BU Related Count",
compute='_compute_bu_related_count'
)
+ manufacturing_id = fields.Many2one('mrp.production', string='Manufacturing Orders')
@api.depends('name')
def _compute_bu_related_count(self):
+ StockPicking = self.env['stock.picking']
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([
+ # Ambil semua BU awal dari PO
+ base_bu = StockPicking.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])
- ])
+ all_bu = base_bu
+ seen_names = set(base_bu.mapped('name'))
+
+ # Loop rekursif untuk mencari seluruh return BU
+ while True:
+ next_bu = StockPicking.search([
+ ('name', 'ilike', 'BU/'),
+ ('origin', 'in', ['Return of %s' % name for name in seen_names])
+ ])
+ next_names = set(next_bu.mapped('name'))
+
+ if not next_names - seen_names:
+ break
+
+ all_bu |= next_bu
+ seen_names |= next_names
+
+ order.bu_related_count = len(all_bu)
- order.bu_related_count = len(base_bu) + len(return_bu)
def action_view_related_bu(self):
self.ensure_one()
+ StockPicking = self.env['stock.picking']
+
# Step 1: cari semua BU pertama (PUT, INT) yang berasal dari PO ini
- base_bu = self.env['stock.picking'].search([
+ base_bu = StockPicking.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])
- ]
+ all_bu = base_bu
+ seen_names = set(base_bu.mapped('name'))
+
+ # Step 2: Loop rekursif cari BU dengan origin 'Return of {name}'
+ while True:
+ next_bu = StockPicking.search([
+ ('name', 'ilike', 'BU/'),
+ ('origin', 'in', ['Return of %s' % name for name in seen_names])
+ ])
+ next_names = set(next_bu.mapped('name'))
+
+ if not next_names - seen_names:
+ break
+
+ all_bu |= next_bu
+ seen_names |= next_names
return {
'name': 'Related BU (INT/PRT/PUT/VRT)',
@@ -144,7 +167,7 @@ class PurchaseOrder(models.Model):
'res_model': 'stock.picking',
'view_mode': 'tree,form',
'target': 'current',
- 'domain': domain,
+ 'domain': [('id', 'in', list(all_bu.ids))],
}
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 109771e9..591951ca 100755
--- a/indoteknik_custom/models/sale_order.py
+++ b/indoteknik_custom/models/sale_order.py
@@ -388,7 +388,9 @@ class SaleOrder(models.Model):
pickings = order.picking_ids.filtered(
lambda p: p.state in ('assigned', 'done') and p.date_reserved and 'BU/PICK/' in (p.name or '')
)
- order.eta_date_reserved = min(pickings.mapped('date_done')) if pickings else False
+ done_dates = [d for d in pickings.mapped('date_done') if d]
+ order.eta_date_reserved = min(done_dates) if done_dates else False
+ # order.eta_date_reserved = min(pickings.mapped('date_done')) if pickings else False
@api.onchange('shipping_cost_covered')
def _onchange_shipping_cost_covered(self):
@@ -845,25 +847,36 @@ class SaleOrder(models.Model):
if total_weight == 0:
raise UserError("Tidak dapat mengestimasi ongkir tanpa berat yang valid.")
- destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id
+ kecamatan_name = self.real_shipping_id.kecamatan_id.name
+ kota_name = self.real_shipping_id.kota_id.name
+ kelurahan_name = self.real_shipping_id.kelurahan_id.name
+
+ destination_subsdistrict_id = self._get_subdistrict_id_from_komerce(kecamatan_name, kota_name, kelurahan_name)
+
+ # destination_subsdistrict_id = self.real_shipping_id.kecamatan_id.rajaongkir_id
if not destination_subsdistrict_id:
raise UserError("Gagal mendapatkan ID kota tujuan.")
result = self._call_rajaongkir_api(total_weight, destination_subsdistrict_id)
+ if not result or not result.get('data'):
+ raise UserError(_("Kurir %s tidak tersedia untuk tujuan ini. Silakan pilih kurir lain.") % self.carrier_id.name)
+
if result:
shipping_options = []
- for courier in result['rajaongkir']['results']:
- for cost_detail in courier['costs']:
- service = cost_detail['service']
- description = cost_detail['description']
- etd = cost_detail['cost'][0]['etd']
- value = cost_detail['cost'][0]['value']
- shipping_options.append((service, description, etd, value, courier['code']))
-
+
+ for cost in result.get('data', []):
+ service = cost.get('service')
+ description = cost.get('description')
+ etd = cost.get('etd', '')
+ value = cost.get('cost', 0)
+ provider = cost.get('code')
+
+ shipping_options.append((service, description, etd, value, provider))
+
self.env["shipping.option"].search([('sale_order_id', '=', self.id)]).unlink()
_logger.info(f"Shipping options: {shipping_options}")
-
+
for service, description, etd, value, provider in shipping_options:
self.env["shipping.option"].create({
"name": service,
@@ -873,19 +886,15 @@ class SaleOrder(models.Model):
"sale_order_id": self.id,
})
-
self.shipping_option_id = self.env["shipping.option"].search([('sale_order_id', '=', self.id)], limit=1).id
_logger.info(f"Shipping option SO ID: {self.shipping_option_id}")
self.message_post(
body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>"
- f"{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}",
+ f"{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]}, Cost: Rp {s[3]}' for s in shipping_options])}",
message_type="comment"
)
-
- # self.message_post(body=f"Estimasi Ongkos Kirim: Rp{self.delivery_amt}<br/>Detail Lain:<br/>{'<br/>'.join([f'Service: {s[0]}, Description: {s[1]}, ETD: {s[2]} hari, Cost: Rp {s[3]}' for s in shipping_options])}", message_type="comment")
-
else:
raise UserError("Gagal mendapatkan estimasi ongkir.")
@@ -1191,25 +1200,30 @@ class SaleOrder(models.Model):
def _call_rajaongkir_api(self, total_weight, destination_subsdistrict_id):
- url = 'https://pro.rajaongkir.com/api/cost'
+ url = 'https://rajaongkir.komerce.id/api/v1/calculate/domestic-cost'
headers = {
'key': '9b1310f644056d84d60b0af6bb21611a',
}
courier = self.carrier_id.name.lower()
data = {
- 'origin': 2127,
- 'originType': 'subdistrict',
+ 'origin': 17656,
+ # 'originType': 'subdistrict',
'destination': int(destination_subsdistrict_id),
- 'destinationType': 'subdistrict',
+ # 'destinationType': 'subdistrict',
'weight': int(total_weight * 1000),
'courier': courier,
}
- response = requests.post(url, headers=headers, data=data)
- if response.status_code == 200:
- return response.json()
- return None
+ try:
+ _logger.info(f"Calling RajaOngkir API with data: {data}")
+ response = requests.post(url, headers=headers, data=data)
+ _logger.info(f"RajaOngkir response: {response.status_code} - {response.text}")
+
+ if response.status_code == 200:
+ return response.json()
+ except Exception as e:
+ _logger.error(f"Exception while calling RajaOngkir: {str(e)}")
def _normalize_city_name(self, city_name):
city_name = city_name.lower()
@@ -1223,37 +1237,82 @@ class SaleOrder(models.Model):
return city_name
- def _get_city_id_by_name(self, city_name):
- url = 'https://pro.rajaongkir.com/api/city'
+ # def _get_city_id_by_name(self, city_name):
+ # url = 'https://pro.rajaongkir.com/api/city'
+ # headers = {
+ # 'key': '9b1310f644056d84d60b0af6bb21611a',
+ # }
+
+ # normalized_city_name = self._normalize_city_name(city_name)
+
+ # response = requests.get(url, headers=headers)
+ # if response.status_code == 200:
+ # city_data = response.json()
+ # for city in city_data['rajaongkir']['results']:
+ # if city['city_name'].lower() == normalized_city_name:
+ # return city['city_id']
+ # return None
+
+ # def _get_subdistrict_id_by_name(self, city_id, subdistrict_name):
+ # url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}'
+ # headers = {
+ # 'key': '9b1310f644056d84d60b0af6bb21611a',
+ # }
+
+ # response = requests.get(url, headers=headers)
+ # if response.status_code == 200:
+ # subdistrict_data = response.json()
+ # for subdistrict in subdistrict_data['rajaongkir']['results']:
+ # subsdistrict_1 = subdistrict['subdistrict_name'].lower()
+ # subsdistrict_2 = subdistrict_name.lower()
+
+ # if subsdistrict_1 == subsdistrict_2:
+ # return subdistrict['subdistrict_id']
+ # return None
+
+ def _get_subdistrict_id_from_komerce(self, kecamatan_name, kota_name, kelurahan_name=None):
+ url = 'https://rajaongkir.komerce.id/api/v1/destination/domestic-destination'
headers = {
'key': '9b1310f644056d84d60b0af6bb21611a',
}
- normalized_city_name = self._normalize_city_name(city_name)
-
- response = requests.get(url, headers=headers)
- if response.status_code == 200:
- city_data = response.json()
- for city in city_data['rajaongkir']['results']:
- if city['city_name'].lower() == normalized_city_name:
- return city['city_id']
- return None
+ if kelurahan_name:
+ search = f"{kelurahan_name} {kecamatan_name} {kota_name}"
+ else:
+ search = f"{kecamatan_name} {kota_name}"
- def _get_subdistrict_id_by_name(self, city_id, subdistrict_name):
- url = f'https://pro.rajaongkir.com/api/subdistrict?city={city_id}'
- headers = {
- 'key': '9b1310f644056d84d60b0af6bb21611a',
+ params = {
+ 'search': search,
+ 'limit': 5
}
- response = requests.get(url, headers=headers)
- if response.status_code == 200:
- subdistrict_data = response.json()
- for subdistrict in subdistrict_data['rajaongkir']['results']:
- subsdistrict_1 = subdistrict['subdistrict_name'].lower()
- subsdistrict_2 = subdistrict_name.lower()
+ try:
+ response = requests.get(url, headers=headers, params=params, timeout=10)
+ if response.status_code == 200:
+ data = response.json().get('data', [])
+ _logger.info(f"[Komerce] Fetched {len(data)} subdistricts for search '{search}'")
+ _logger.info(f"[Komerce] Response: {data}")
+
+ normalized_kota = self._normalize_city_name(kota_name)
+
+ for item in data:
+ match_kelurahan = (
+ not kelurahan_name or
+ item.get('subdistrict_name', '').lower() == kelurahan_name.lower()
+ )
+ if (
+ match_kelurahan and
+ item.get('district_name', '').lower() == kecamatan_name.lower() and
+ item.get('city_name', '').lower() == normalized_kota
+ ):
+ return item.get('id')
+
+ _logger.warning(f"[Komerce] No match for '{kecamatan_name}' in city '{kota_name}' with kelurahan '{kelurahan_name}'")
+ else:
+ _logger.error(f"[Komerce] HTTP Error {response.status_code}: {response.text}")
+ except Exception as e:
+ _logger.error(f"[Komerce] Exception: {e}")
- if subsdistrict_1 == subsdistrict_2:
- return subdistrict['subdistrict_id']
return None
def _compute_type_promotion(self):
diff --git a/indoteknik_custom/views/mrp_production.xml b/indoteknik_custom/views/mrp_production.xml
index 3de52a08..5057415f 100644
--- a/indoteknik_custom/views/mrp_production.xml
+++ b/indoteknik_custom/views/mrp_production.xml
@@ -11,7 +11,7 @@
<field name="bom_id" position="after">
<field name="desc"/>
<field name="sale_order"/>
- <field name="is_po"/>
+ <field name="is_po" readonly="1"/>
</field>
<xpath expr="//form/sheet/notebook/page/field[@name='move_raw_ids']/tree/field[@name='product_uom_qty']" position="before">
<field name="vendor_id"/>
diff --git a/indoteknik_custom/views/purchase_order.xml b/indoteknik_custom/views/purchase_order.xml
index 530fd115..dae23eed 100755
--- a/indoteknik_custom/views/purchase_order.xml
+++ b/indoteknik_custom/views/purchase_order.xml
@@ -87,7 +87,8 @@
<field name="payment_term_id"/>
<field name="total_cost_service" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/>
<field name="total_delivery_amt" attrs="{'required': [('partner_id', 'in', [9688, 29712])]}"/>
- <field name="product_bom_id"/>
+ <field name="product_bom_id" attrs="{'invisible': [('product_bom_id', '=', None)]}"/>
+ <field name="manufacturing_id" attrs="{'invisible': [('product_bom_id', '=', None)]}"/>
<!-- <field name="move_id" domain="[('move_type','=','entry')]" context="{'form_view_ref': 'account.view_move_form'}" options="{'no_create': True}"/> -->
</field>
<field name="amount_total" position="after">
diff --git a/indoteknik_custom/views/res_partner.xml b/indoteknik_custom/views/res_partner.xml
index 6115587b..ac4d0364 100644
--- a/indoteknik_custom/views/res_partner.xml
+++ b/indoteknik_custom/views/res_partner.xml
@@ -108,6 +108,13 @@
<xpath expr="//field[@name='property_supplier_payment_term_id']" position="attributes">
<attribute name="readonly">1</attribute>
</xpath>
+ <xpath expr="//notebook/page[@name='accounting']" position="inside">
+ <group string="Aging Info">
+ <field name="avg_aging" readonly="1"/>
+ <field name="payment_difficulty" attrs="{'readonly': [('parent_id', '!=', False)]}" />
+ <field name="payment_history_url" readonly="1" />
+ </group>
+ </xpath>
<notebook>
<page string="Pengajuan Tempo">
<!-- Informasi Usaha Section -->
@@ -181,11 +188,11 @@
<field name="dokumen_pengiriman_input"/>
<field name="dokumen_invoice"/>
</group>
- <group string="Aging Info">
+ <!-- <group string="Aging Info">
<field name="avg_aging" readonly="1"/>
<field name="payment_difficulty" attrs="{'readonly': [('parent_id', '!=', False)]}" />
<field name="payment_history_url" readonly="1" />
- </group>
+ </group> -->
</group>
<!-- Supplier Lines Section -->