summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMqdd <ahmadmiqdad27@gmail.com>2026-02-24 09:57:15 +0700
committerMqdd <ahmadmiqdad27@gmail.com>2026-02-24 09:57:15 +0700
commit0b64d465d109392cdb4e634b1ccfffa56935d5e5 (patch)
treeb19f9d47e28e7bb8dfdfd9dc0de614c5bab9385d
parent79471b69ffc683c3c860ecd6456f638750a8c81f (diff)
parent18bdbf118d2f582ac0c520a9b4cc5b61fe88488f (diff)
Merge branch 'odoo-backup' of https://bitbucket.org/altafixco/indoteknik-addons into odoo-backup
-rw-r--r--indoteknik_custom/models/advance_payment_request.py96
-rw-r--r--indoteknik_custom/models/keywords.py43
-rw-r--r--indoteknik_custom/models/price_group.py20
-rwxr-xr-xindoteknik_custom/models/product_template.py31
-rwxr-xr-xindoteknik_custom/models/purchase_pricelist.py7
-rwxr-xr-xindoteknik_custom/models/sale_order.py2
-rwxr-xr-xindoteknik_custom/security/ir.model.access.csv1
-rw-r--r--indoteknik_custom/views/account_move_line.xml3
-rw-r--r--indoteknik_custom/views/advance_payment_request.xml24
-rw-r--r--indoteknik_custom/views/advance_payment_settlement.xml14
10 files changed, 182 insertions, 59 deletions
diff --git a/indoteknik_custom/models/advance_payment_request.py b/indoteknik_custom/models/advance_payment_request.py
index 8cadb1b6..039d18a5 100644
--- a/indoteknik_custom/models/advance_payment_request.py
+++ b/indoteknik_custom/models/advance_payment_request.py
@@ -155,6 +155,18 @@ class AdvancePaymentRequest(models.Model):
compute='_compute_is_current_user_ap'
)
+ estimate_line_ids = fields.One2many('advance.payment.request.estimate.line', 'request_id', string='Rincian Estimasi')
+
+ @api.constrains('nominal', 'estimate_line_ids')
+ def _check_nominal_vs_estimate_total(self):
+ for rec in self:
+ if rec.type_request == 'pum':
+ if not rec.estimate_line_ids:
+ raise UserError("Rincian estimasi wajib diisi untuk PUM. Silakan tambahkan rincian estimasi.")
+ total_estimate = sum(line.nominal for line in rec.estimate_line_ids)
+ if round(total_estimate, 2) != round(rec.nominal, 2):
+ raise UserError("Total estimasi harus sama dengan nominal PUM. Silakan sesuaikan rincian estimasi atau nominal PUM.")
+
@api.onchange('grand_total_reimburse', 'type_request')
def _onchange_reimburse_line_update_nominal(self):
if self.type_request == 'reimburse':
@@ -751,7 +763,7 @@ class AdvancePaymentRequest(models.Model):
pum_ids = self.search([
('user_id', '=', user.id),
- ('status', '!=', 'reject'),
+ ('status', '!=', 'cancel'),
('type_request', '=', 'pum')
])
@@ -911,6 +923,35 @@ class AdvancePaymentUsageLine(models.Model):
compute='_compute_is_current_user_ap'
)
+ category_usage = fields.Selection([
+ ('parkir', 'Parkir'),
+ ('tol', 'Tol'),
+ ('bbm', 'BBM'),
+ ('kuli', 'Kuli'),
+ ('konsumsi', 'Konsumsi'),
+ ('lain_lain', 'Lain-lain'),
+ ], string='Kategori System', compute='_compute_category_usage')
+
+ @api.depends('account_id')
+ def _compute_category_usage(self):
+ for rec in self:
+ if not rec.account_id:
+ rec.category_usage = False
+ continue
+ name = rec.account_id.name.lower()
+ if 'bbm' in name or 'bahan bakar' in name:
+ rec.category_usage = 'bbm'
+ elif 'tol' in name:
+ rec.category_usage = 'tol'
+ elif 'parkir' in name:
+ rec.category_usage = 'parkir'
+ elif 'kuli' in name or 'bongkar' in name:
+ rec.category_usage = 'kuli'
+ elif 'konsumsi' in name or 'makan' in name or 'minum' in name:
+ rec.category_usage = 'konsumsi'
+ else:
+ rec.category_usage = 'lain_lain'
+
def _compute_is_current_user_ap(self):
ap_user_ids = [23, 9468, 16729]
is_ap = self.env.user.id in ap_user_ids
@@ -1144,6 +1185,11 @@ class AdvancePaymentSettlement(models.Model):
string="Is Current User AP",
compute='_compute_is_current_user_ap'
)
+ pum_estimate_line_ids = fields.One2many(
+ related='pum_id.estimate_line_ids',
+ string='Rincian Estimasi PUM',
+ readonly=True
+ )
def _compute_is_current_user_ap(self):
ap_user_ids = [23, 9468, 16729]
@@ -1612,4 +1658,50 @@ class CreateReimburseCabWizard(models.TransientModel):
'type': 'ir.actions.act_window',
'res_id': move.id,
'target': 'current',
- } \ No newline at end of file
+ }
+
+class AdvancePaymentRequestEstimateLine(models.Model):
+ _name = 'advance.payment.request.estimate.line'
+ _description = 'Advance Payment Request Estimate Line'
+
+ request_id = fields.Many2one('advance.payment.request', string='Request')
+ category_estimate = fields.Selection([
+ ('parkir', 'Parkir'),
+ ('tol', 'Tol'),
+ ('bbm', 'BBM'),
+ ('kuli', 'Kuli'),
+ ('konsumsi', 'Konsumsi'),
+ ('lain_lain', 'Lain-lain'),
+ ], string='Kategori Estimasi', required=True)
+ description = fields.Text(string='Description', help='Deskripsi tambahan untuk estimasi biaya yang diperlukan.')
+ nominal = fields.Float(string='Nominal Estimasi', required=True, help='Masukkan nominal estimasi untuk kategori ini (tidak mesti akurat, hanya untuk gambaran umum).')
+ currency_id = fields.Many2one(related='request_id.currency_id')
+
+ total_actual = fields.Float(string='Nominal Realisasi', compute='_compute_actual_data')
+ frequency = fields.Integer(string='Qty Realisasi', compute='_compute_actual_data')
+
+ @api.depends('request_id.settlement_ids.penggunaan_line_ids.nominal',
+ 'request_id.settlement_ids.penggunaan_line_ids.account_id')
+ def _compute_actual_data(self):
+ for rec in self:
+ total_act = 0
+ freq = 0
+ if rec.request_id and rec.request_id.settlement_ids:
+ all_usage_lines = rec.request_id.settlement_ids.mapped('penggunaan_line_ids')
+ valid_lines = all_usage_lines.filtered(lambda l: l.account_id and l.category_usage)
+ planned_category = rec.request_id.estimate_line_ids.mapped('category_estimate')
+ if rec.category_estimate == 'lain_lain':
+ matched_lines = valid_lines.filtered(
+ lambda l: l.category_usage == 'lain_lain' or \
+ l.category_usage not in planned_category
+ )
+ else:
+ matched_lines = valid_lines.filtered(
+ lambda l: l.category_usage == rec.category_estimate
+ )
+
+ total_act = sum(matched_lines.mapped('nominal'))
+ freq = len(matched_lines)
+ rec.total_actual = total_act
+ rec.frequency = freq
+
diff --git a/indoteknik_custom/models/keywords.py b/indoteknik_custom/models/keywords.py
index 5b7da705..3fa9dd72 100644
--- a/indoteknik_custom/models/keywords.py
+++ b/indoteknik_custom/models/keywords.py
@@ -133,7 +133,7 @@ class Keywords(models.Model):
def _onchange_solr_flag(self):
"""Set solr_flag=2 when tracked fields change to trigger queue sync"""
for record in self:
- if not record.skip:
+ if len(record.product_ids) > 0:
record.solr_flag = 2
def solr_flag_to_queue(self, limit=500):
@@ -161,10 +161,18 @@ class Keywords(models.Model):
return True
def _sync_keywords_queue_callback(self):
- """Callback method executed by apache.solr.queue - syncs keyword data to Solr"""
- documents = []
+ success_keywords = self.browse()
+
for keyword in self:
+ if not keyword.product_ids:
+ _logger.info(
+ 'Skipping Solr sync for keyword "%s" - no products found',
+ keyword.keywords
+ )
+ continue
+
searchkey = (keyword.keywords or '').strip().lower().replace(' ', '-')
+
try:
doc = {
'id': keyword.id,
@@ -173,13 +181,19 @@ class Keywords(models.Model):
'url_s': keyword.url,
'product_ids_is': [p.product_tmpl_id.id for p in keyword.product_ids],
}
- documents.append(doc)
+
+ solr.add([doc])
+
+ success_keywords |= keyword
+
except Exception as e:
- _logger.error('failed %s', e)
- _logger.error('doc data: %s', doc)
+ _logger.error(
+ "Solr sync failed for keyword ID %s: %s",
+ keyword.id, e
+ )
- if documents:
- solr.add(documents)
+ if success_keywords:
+ success_keywords.write({'solr_flag': 0})
return True
@@ -194,6 +208,11 @@ class Keywords(models.Model):
documents = []
for keyword in keywords:
+ # Skip syncing if product count is 0
+ if len(keyword.product_ids) == 0:
+ _logger.info('Skipping Solr sync for keyword "%s" - no products found', keyword.keywords)
+ continue
+
searchkey = (keyword.keywords or '').strip().lower().replace(' ', '-')
try:
doc = {
@@ -223,10 +242,12 @@ class Keywords(models.Model):
def write(self, vals):
result = super().write(vals)
- tracked_fields = ['keywords', 'category_id', 'product_ids']
+ tracked_fields = ['keywords', 'category_id', 'product_ids', 'skip', 'name']
neded_sync = any(field in vals for field in tracked_fields)
- if neded_sync and self.skip == False and len(self.product_ids) > 0:
+ if neded_sync:
for record in self:
- record.solr_flag = 2
+ # Only flag for sync if there are products
+ if len(record.product_ids) > 0:
+ record.solr_flag = 2
return result
diff --git a/indoteknik_custom/models/price_group.py b/indoteknik_custom/models/price_group.py
index fce78fff..1a4f1bd6 100644
--- a/indoteknik_custom/models/price_group.py
+++ b/indoteknik_custom/models/price_group.py
@@ -10,16 +10,16 @@ class PriceGroup(models.Model):
name = fields.Char(string='Name')
pricelist_id = fields.Many2one('product.pricelist', string='Price List')
- group1 = fields.Float(string='Kelompok 1 (%)')
- group2 = fields.Float(string='Kelompok 2 (%)')
- group3 = fields.Float(string='Kelompok 3 (%)')
- group4 = fields.Float(string='Kelompok 4 (%)')
- group5 = fields.Float(string='Kelompok 5 (%)')
- 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 (%)')
+ group1 = fields.Float(string='Kelompok 1 (%)', digits=(16, 12))
+ group2 = fields.Float(string='Kelompok 2 (%)', digits=(16, 12))
+ group3 = fields.Float(string='Kelompok 3 (%)', digits=(16, 12))
+ group4 = fields.Float(string='Kelompok 4 (%)', digits=(16, 12))
+ group5 = fields.Float(string='Kelompok 5 (%)', digits=(16, 12))
+ group6 = fields.Float(string='Kelompok 6 (%)', digits=(16, 12))
+ group7 = fields.Float(string='Kelompok 7 (%)', digits=(16, 12))
+ group8 = fields.Float(string='Kelompok 8 (%)', digits=(16, 12))
+ group9 = fields.Float(string='Kelompok 9 (%)', digits=(16, 12))
+ group10 = fields.Float(string='Kelompok 10 (%)', digits=(16, 12))
def collect_price_group(self):
PRICE_GROUP_ID = {
diff --git a/indoteknik_custom/models/product_template.py b/indoteknik_custom/models/product_template.py
index 397bd06d..e10b4de2 100755
--- a/indoteknik_custom/models/product_template.py
+++ b/indoteknik_custom/models/product_template.py
@@ -94,7 +94,6 @@ class ProductTemplate(models.Model):
if self.env.user.id not in users_in_group.mapped('id') and active_model == None:
raise UserError('Hanya MD yang bisa membuat Product')
result = super(ProductTemplate, self).create(vals)
- self.env['product.product']._add_product_to_keywords(result)
return result
# def write(self, values):
@@ -895,11 +894,6 @@ class ProductTemplate(models.Model):
# Log changes
self._log_field_changes_product(vals, old_values)
- # Add product to keywords
- keyword_trigger = ['name', 'website_description', 'unpublished']
- if any(field in vals for field in keyword_trigger):
- for product in self:
- self.env['product.product']._add_product_to_keywords(product)
return result
# def write(self, vals):
@@ -946,26 +940,7 @@ class ProductProduct(models.Model):
qr_code_variant = fields.Binary("QR Code Variant", compute='_compute_qr_code_variant')
qty_pcs_box = fields.Float("Pcs Box")
barcode_box = fields.Char("Barcode Box")
- keyword_id = fields.Many2one('keywords', string='Keyword')
-
- def _add_product_to_keywords(self,product):
- keywords_model = self.env['keywords']
- if not product:
- return False
-
- for product in self:
- match_keywords = keywords_model.search([
- '|',
- ('name', 'ilike', product.name),
- ('keywords', 'ilike', product.website_description)
- ])
-
- for kw in match_keywords.filtered(lambda k: not k.skip):
- if not self.unpublished and product.id not in kw.product_ids.ids:
- kw.write({'product_ids': [(4, product.id)]})
-
- return True
-
+ # keyword_id = fields.Many2one('keywords', string='Keyword')
has_magento = fields.Boolean(string='Has Magento?', default=False, readonly=True)
def generate_product_sla(self):
@@ -988,7 +963,6 @@ class ProductProduct(models.Model):
if self.env.user.id not in users_in_group.mapped('id') and active_model == None:
raise UserError('Hanya MD yang bisa membuat Product')
result = super(ProductProduct, self).create(vals)
- self._add_product_to_keywords(result)
return result
# def write(self, values):
@@ -1355,9 +1329,6 @@ class ProductProduct(models.Model):
]
# pake ini kalau mau Cek semua field
# if vals:
- trigger_fields = ['name', 'website_description', 'unpublished']
- if any(f in vals for f in trigger_fields):
- self._add_product_to_keywords(vals)
if any(field in vals for field in tracked_fields):
old_values = self._collect_old_values(vals)
result = super().write(vals)
diff --git a/indoteknik_custom/models/purchase_pricelist.py b/indoteknik_custom/models/purchase_pricelist.py
index b3a473b6..83e06f55 100755
--- a/indoteknik_custom/models/purchase_pricelist.py
+++ b/indoteknik_custom/models/purchase_pricelist.py
@@ -118,15 +118,16 @@ class PurchasePricelist(models.Model):
product_domain = [('product_id', '=', rec.product_id.id)]
markup_pricelist = price_group['markup'].pricelist_id
- base_price = price_incl + (price_incl * markup_percentage / 100)
+ # base_price = price_incl + (price_incl * markup_percentage / 100)
+ base_price = round(price_incl + (price_incl * markup_percentage / 100), 12)
base_prod_pricelist = self.env['product.pricelist.item'].search(product_domain + [('pricelist_id', '=', markup_pricelist.id)], limit=1)
base_prod_pricelist.fixed_price = base_price
tier_percentages = [price_group[f'tier_{i}'][product_group] for i in range(1, 6)]
for i, tier_percentage in enumerate(tier_percentages):
tier_pricelist = price_group[f'tier_{i + 1}'].pricelist_id
- tier_price = price_incl + (price_incl * tier_percentage / 100)
- tier_perc = (base_price - tier_price) / base_price * 100
+ tier_price = round(price_incl + (price_incl * tier_percentage / 100), 12)
+ tier_perc = round((base_price - tier_price) / base_price * 100, 12)
tier_prod_pricelist = self.env['product.pricelist.item'].search(product_domain + [('pricelist_id', '=', tier_pricelist.id)], limit=1)
tier_prod_pricelist.price_discount = tier_perc
diff --git a/indoteknik_custom/models/sale_order.py b/indoteknik_custom/models/sale_order.py
index 90cd5fa2..e6fc4732 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','ninja','lion','rara','sicepat','jnt','pos','idexpress','rpx','wahana','jdl','anteraja','sap','paxel','borzo'
+ 'gojek','grab','deliveree','lalamove','jne','ninja','lion','rara','sicepat','jnt','idexpress','rpx','wahana','jdl','anteraja','sap','paxel','borzo'
]
@api.onchange('carrier_id')
diff --git a/indoteknik_custom/security/ir.model.access.csv b/indoteknik_custom/security/ir.model.access.csv
index 4ffefc68..b6583ed5 100755
--- a/indoteknik_custom/security/ir.model.access.csv
+++ b/indoteknik_custom/security/ir.model.access.csv
@@ -200,6 +200,7 @@ access_advance_payment_usage_line,access.advance.payment.usage.line,model_advanc
access_advance_payment_create_bill,access.advance.payment.create.bill,model_advance_payment_create_bill,,1,1,1,1
access_create_reimburse_cab_wizard_user,create.reimburse.cab.wizard user,model_create_reimburse_cab_wizard,,1,1,1,1
access_advance_payment_cancel_wizard,advance.payment.cancel.wizard,model_advance_payment_cancel_wizard,,1,1,1,1
+access_advance_payment_request_estimate_line,advance.payment.request.estimate.line,model_advance_payment_request_estimate_line,,1,1,1,1
access_purchasing_job_seen,purchasing.job.seen,model_purchasing_job_seen,,1,1,1,1
access_tukar_guling_all_users,tukar.guling.all.users,model_tukar_guling,base.group_user,1,1,1,1
diff --git a/indoteknik_custom/views/account_move_line.xml b/indoteknik_custom/views/account_move_line.xml
index 838596c8..346494f3 100644
--- a/indoteknik_custom/views/account_move_line.xml
+++ b/indoteknik_custom/views/account_move_line.xml
@@ -9,6 +9,9 @@
<!-- <xpath expr="//page[@id='aml_tab']/field[@name='line_ids']" position="attributes">
<attribute name="attrs">{'readonly': [('refund_id','!=',False)]}</attribute>
</xpath> -->
+ <xpath expr="//field[@name='line_ids']/tree/field[@name='credit']" position="after">
+ <field name="date_maturity" optional="hide"/>
+ </xpath>
<xpath expr="//page[@id='aml_tab']/field[@name='line_ids']/tree/field[@name='currency_id']" position="before">
<field name="is_required" invisible="1"/>
</xpath>
diff --git a/indoteknik_custom/views/advance_payment_request.xml b/indoteknik_custom/views/advance_payment_request.xml
index 340e0caf..4faf905e 100644
--- a/indoteknik_custom/views/advance_payment_request.xml
+++ b/indoteknik_custom/views/advance_payment_request.xml
@@ -134,8 +134,28 @@
<br/>
</group>
</group>
- <notebook attrs="{'invisible': [('type_request', '!=', 'reimburse')]}">
- <page string="Rincian Reimburse">
+ <notebook>
+ <page string="Rincian Estimasi PUM" attrs="{'invisible': [('type_request', '!=', 'pum')]}">
+ <p style="font-size: 12px; color: grey; font-style: italic">*Masukkan estimasi alokasi biaya sebagai gambaran rencana penggunaan dana, tidak harus diisi dengan nominal yang akurat</p>
+ <field name="estimate_line_ids">
+ <tree>
+ <field name="category_estimate"/>
+ <field name="description"/>
+ <field name="nominal" sum="Total"/>
+ <field name="currency_id" invisible="1"/>
+ </tree>
+ <form>
+ <group col="2">
+ <field name="request_id" invisible="1"/>
+ <field name="category_estimate"/>
+ <field name="description" placeholder="Deskripsi tambahan untuk rincian estimasi..."/>
+ <field name="nominal"/>
+ <field name="currency_id" invisible="1"/>
+ </group>
+ </form>
+ </field>
+ </page>
+ <page string="Rincian Reimburse" attrs="{'invisible': [('type_request', '!=', 'reimburse')]}">
<field name="reimburse_line_ids">
<tree>
<field name="sequence" widget="handle"/>
diff --git a/indoteknik_custom/views/advance_payment_settlement.xml b/indoteknik_custom/views/advance_payment_settlement.xml
index a8bf1de7..352c5b96 100644
--- a/indoteknik_custom/views/advance_payment_settlement.xml
+++ b/indoteknik_custom/views/advance_payment_settlement.xml
@@ -118,12 +118,26 @@
<group string="Finance">
<field name="is_current_user_ap" invisible="1"/>
<field name="account_id" attrs="{'readonly': [('is_current_user_ap', '=', False)]}"/>
+ <field name="category_usage" invisible="1"/>
<field name="done_attachment" attrs="{'readonly': [('is_current_user_ap', '=', False)]}"/>
</group>
</group>
</form>
</field>
</page>
+ <page string="Rincian Estimasi PUM">
+ <p style="font-size: 12px; color: grey; font-style: italic">*Rincian estimasi PUM ini hanya sebagai gambaran umum untuk realisasi yang dilakukan, tidak harus diisi dengan nominal yang akurat.</p>
+ <field name="pum_estimate_line_ids" nolabel="1">
+ <tree>
+ <field name="category_estimate"/>
+ <field name="description"/>
+ <field name="nominal" sum="Total Estimasi"/>
+ <field name="frequency"/>
+ <field name="total_actual" sum="Total Actual"/>
+ <field name="currency_id" invisible="1"/>
+ </tree>
+ </field>
+ </page>
</notebook>
<div style="text-align:right;">