Pengenaan icon
This commit is contained in:
@@ -10,6 +10,7 @@
|
||||
'stock',
|
||||
'account',
|
||||
'om_account_asset',
|
||||
'asa_simpin_syariah',
|
||||
'master_sapi',
|
||||
'kandang',
|
||||
],
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -2,6 +2,7 @@
|
||||
<odoo>
|
||||
<menuitem id="menu_dairy_management_root"
|
||||
name="Dairy Asset Management"
|
||||
web_icon="grt_asset_dairy_management,static/description/icon.png"
|
||||
sequence="25"/>
|
||||
|
||||
<menuitem id="menu_dairy_management_cows"
|
||||
|
||||
@@ -538,14 +538,18 @@ Setiap rule minimal berisi:
|
||||
- `Team Sales`
|
||||
- `Wilayah` pada level `wilayah.kecamatan`
|
||||
- `Shipping Product`
|
||||
- `Tarif per Kg`
|
||||
|
||||
Perilaku backend:
|
||||
|
||||
- backend hanya menambahkan ongkir otomatis untuk Sales Order yang dibuat dari endpoint frontend
|
||||
- backend mencari rule berdasarkan kombinasi `team_id + wilayah_id + company`
|
||||
- jika rule ditemukan, backend menambahkan 1 line produk ongkir otomatis
|
||||
- harga ongkir mengikuti pricelist Sales Order atau `list_price` produk ongkir
|
||||
- backend menghitung total berat dari semua line produk: `qty x berat produk`
|
||||
- nominal ongkir dihitung dengan rumus `total_kg x tarif_per_kg`
|
||||
- line ongkir otomatis menyimpan ringkasan berat total dan tarif per kg pada deskripsi line
|
||||
- jika rule tidak ditemukan, pembuatan Sales Order akan ditolak
|
||||
- jika total berat produk `0`, pembuatan Sales Order akan ditolak
|
||||
|
||||
Catatan untuk tim frontend:
|
||||
|
||||
@@ -675,6 +679,7 @@ Minimal kirim `partner_id` atau `customer_qr_ref`.
|
||||
- `order_line` wajib minimal 1 item
|
||||
- setiap line wajib punya `product_id` dan `product_uom_qty > 0`
|
||||
- backend otomatis menambahkan line biaya pengiriman untuk order frontend berdasarkan rule `team_id + wilayah_id`
|
||||
- backend menghitung nominal line ongkir dari `total berat produk x tarif per kg` pada rule
|
||||
- jika rule ongkir frontend tidak ditemukan, pembuatan order akan ditolak
|
||||
|
||||
### Endpoint Draft Order Berdasarkan Jenis Bon
|
||||
@@ -869,7 +874,9 @@ export async function getJsonSession(url) {
|
||||
- pilih endpoint draft order sesuai jenis transaksi yang dipilih user di frontend
|
||||
- `wilayah_id` wajib dikirim untuk semua endpoint create draft order frontend
|
||||
- backend akan lookup rule ongkir frontend berdasarkan kombinasi `team_id` dan `wilayah_id`
|
||||
- jika rule cocok, backend otomatis menambah 1 line produk ongkir dengan harga mengikuti pricelist atau `list_price` produk ongkir
|
||||
- backend menghitung total berat dari seluruh line produk non-ongkir menggunakan field `weight` pada produk
|
||||
- jika rule cocok, backend otomatis menambah 1 line produk ongkir dengan nominal `total_kg x tarif_per_kg`
|
||||
- semua produk yang dipakai untuk perhitungan ongkir berbasis kilogram harus memiliki field `weight` yang terisi benar
|
||||
- Terms and Conditions di backend disimpan pada field `note` di `sale.order`
|
||||
- `customer-qr-by-id` cocok jika frontend hanya perlu string referensi QR
|
||||
- `customer-qr-payload-by-id` cocok jika frontend ingin langsung render QR dan menyimpan metadata QR sekaligus dari `customer_id`
|
||||
|
||||
@@ -24,6 +24,7 @@ Buat 1 rule contoh:
|
||||
- `Team Sales`: team yang dipakai frontend
|
||||
- `Wilayah`: kecamatan yang mewakili kelompok petani/customer
|
||||
- `Shipping Product`: produk biaya pengiriman
|
||||
- `Tarif per Kg`: misalnya `1500`
|
||||
|
||||
## Payload Contoh
|
||||
|
||||
@@ -121,7 +122,8 @@ Saat request berhasil:
|
||||
- field `frontend_kecamatan_id` terisi sesuai `wilayah_id`
|
||||
- order line asli dari frontend tetap ada
|
||||
- sistem otomatis menambahkan 1 line produk ongkir
|
||||
- harga ongkir mengikuti pricelist order atau `list_price` produk ongkir
|
||||
- nominal ongkir dihitung dari `total berat produk x tarif per kg`
|
||||
- deskripsi line ongkir menampilkan ringkasan berat total dan tarif per kg
|
||||
|
||||
## Kasus Error Yang Perlu Diuji
|
||||
|
||||
@@ -129,6 +131,8 @@ Saat request berhasil:
|
||||
- `wilayah_id` tidak valid
|
||||
- kombinasi `team_id + wilayah_id` belum punya rule
|
||||
- produk ongkir pada rule tidak valid atau `sale_ok = False`
|
||||
- `Tarif per Kg` pada rule masih `0`
|
||||
- semua produk pada order belum punya berat sehingga total kilogram = `0`
|
||||
|
||||
## Cek di Backend
|
||||
|
||||
|
||||
@@ -42,6 +42,13 @@ class FrontendShippingRule(models.Model):
|
||||
domain="[('sale_ok', '=', True)]",
|
||||
ondelete="restrict",
|
||||
)
|
||||
shipping_price_per_kg = fields.Float(
|
||||
string="Tarif per Kg",
|
||||
required=True,
|
||||
default=0.0,
|
||||
digits="Product Price",
|
||||
help="Tarif ongkos kirim yang akan dikalikan dengan total berat produk pada order frontend.",
|
||||
)
|
||||
|
||||
_sql_constraints = [
|
||||
(
|
||||
@@ -51,16 +58,18 @@ class FrontendShippingRule(models.Model):
|
||||
),
|
||||
]
|
||||
|
||||
@api.depends("team_id", "wilayah_kecamatan_id", "shipping_product_id")
|
||||
@api.depends("team_id", "wilayah_kecamatan_id", "shipping_product_id", "shipping_price_per_kg")
|
||||
def _compute_name(self):
|
||||
for rule in self:
|
||||
parts = [rule.team_id.name, rule.wilayah_kecamatan_id.name, rule.shipping_product_id.display_name]
|
||||
rule.name = " / ".join([part for part in parts if part])
|
||||
|
||||
@api.constrains("company_id", "team_id", "shipping_product_id")
|
||||
@api.constrains("company_id", "team_id", "shipping_product_id", "shipping_price_per_kg")
|
||||
def _check_company_consistency(self):
|
||||
for rule in self:
|
||||
if rule.team_id.company_id and rule.team_id.company_id != rule.company_id:
|
||||
raise ValidationError(_("Team Sales company must match the shipping rule company."))
|
||||
if rule.shipping_product_id.company_id and rule.shipping_product_id.company_id != rule.company_id:
|
||||
raise ValidationError(_("Shipping product company must match the shipping rule company."))
|
||||
if rule.shipping_price_per_kg <= 0:
|
||||
raise ValidationError(_("Tarif per Kg must be greater than 0."))
|
||||
|
||||
@@ -159,17 +159,14 @@ class SaleOrder(models.Model):
|
||||
vals["analytic_account_id"] = self.analytic_account_id.id
|
||||
return vals
|
||||
|
||||
def _get_frontend_shipping_price(self, product, qty=1.0):
|
||||
def _get_frontend_shipping_total_weight(self):
|
||||
self.ensure_one()
|
||||
pricelist = self.pricelist_id
|
||||
if pricelist:
|
||||
try:
|
||||
return pricelist.with_context(uom=product.uom_id.id).get_product_price(
|
||||
product, qty, self.partner_id
|
||||
)
|
||||
except Exception:
|
||||
pass
|
||||
return product.lst_price
|
||||
total_weight = 0.0
|
||||
for line in self.order_line:
|
||||
if line.display_type or line.is_frontend_shipping_line or not line.product_id:
|
||||
continue
|
||||
total_weight += line.product_uom_qty * (line.product_id.weight or 0.0)
|
||||
return total_weight
|
||||
|
||||
def _apply_frontend_shipping_rule(self):
|
||||
for order in self:
|
||||
@@ -200,17 +197,34 @@ class SaleOrder(models.Model):
|
||||
raise UserError(
|
||||
_("Shipping product '%s' is not available for sale.") % product.display_name
|
||||
)
|
||||
if rule.shipping_price_per_kg <= 0:
|
||||
raise UserError(
|
||||
_("Shipping rule for Team Sales '%s' and Wilayah '%s' must define Tarif per Kg greater than 0.")
|
||||
% (order.team_id.name, order.frontend_kecamatan_id.name)
|
||||
)
|
||||
|
||||
total_weight = order._get_frontend_shipping_total_weight()
|
||||
if total_weight <= 0:
|
||||
raise UserError(
|
||||
_("Total product weight must be greater than 0 to calculate frontend shipping cost.")
|
||||
)
|
||||
|
||||
taxes = product.taxes_id.filtered(
|
||||
lambda tax: not tax.company_id or tax.company_id == order.company_id
|
||||
)
|
||||
price_unit = total_weight * rule.shipping_price_per_kg
|
||||
line_name = _("%s\nBerat total: %.2f Kg x Tarif: %.2f") % (
|
||||
product.get_product_multiline_description_sale() or product.display_name,
|
||||
total_weight,
|
||||
rule.shipping_price_per_kg,
|
||||
)
|
||||
line_vals = {
|
||||
"order_id": order.id,
|
||||
"product_id": product.id,
|
||||
"name": product.get_product_multiline_description_sale() or product.display_name,
|
||||
"name": line_name,
|
||||
"product_uom_qty": 1.0,
|
||||
"product_uom": product.uom_id.id,
|
||||
"price_unit": order._get_frontend_shipping_price(product, qty=1.0),
|
||||
"price_unit": price_unit,
|
||||
"tax_id": [(6, 0, taxes.ids)],
|
||||
"is_frontend_shipping_line": True,
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
<field name="team_id"/>
|
||||
<field name="wilayah_kecamatan_id"/>
|
||||
<field name="shipping_product_id"/>
|
||||
<field name="shipping_price_per_kg"/>
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
@@ -28,6 +29,7 @@
|
||||
<field name="team_id"/>
|
||||
<field name="wilayah_kecamatan_id"/>
|
||||
<field name="shipping_product_id"/>
|
||||
<field name="shipping_price_per_kg"/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
@@ -42,6 +44,7 @@
|
||||
<field name="team_id"/>
|
||||
<field name="wilayah_kecamatan_id"/>
|
||||
<field name="shipping_product_id"/>
|
||||
<field name="shipping_price_per_kg"/>
|
||||
<field name="business_category_id"/>
|
||||
<field name="company_id"/>
|
||||
<filter string="Active" name="active" domain="[('active', '=', True)]"/>
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Reference in New Issue
Block a user