HEX
Server: nginx/1.18.0
System: Linux mail.dakarash.co.id 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User: www-data (33)
PHP: 8.1.2-1ubuntu2.23
Disabled: NONE
Upload Files
File: /home/django/apps/cargochains/sales/forms_job_order.bak
# sales/forms_job_order.py

from decimal import Decimal, InvalidOperation

from django import forms

from .job_order_model import JobOrder
from partners.models import Customer  # proxy customer yang sudah om buat



class JobOrderForm(forms.ModelForm):
    # override field angka jadi CharField supaya bisa pakai format "1.000,00"
    total_amount = forms.CharField(required=False)
    tax_amount = forms.CharField(required=False)
    grand_total = forms.CharField(required=False)

    # kurs & total IDR juga kita tampilkan sebagai text (format Indonesia)
    kurs_idr = forms.CharField(required=False, label="Kurs IDR")
    total_in_idr = forms.CharField(required=False, label="Total (IDR)")

    class Meta:
        model = JobOrder
        fields = "__all__"
        # tidak ditampilkan di form:
        exclude = ["created", "modified", "is_pph", "pph_amount", "sales_user","number"]

        widgets = {
            "job_date": forms.DateInput(
                attrs={"type": "date", "class": "form-control form-control-sm"}
            ),

            "service": forms.Select(attrs={"class": "form-select form-select-sm"}),
            "customer": forms.Select(attrs={"class": "form-select form-select-sm"}),

            "cargo_description": forms.TextInput(
                attrs={"class": "form-control form-control-sm"}
            ),
            "quantity": forms.NumberInput(
                attrs={"class": "form-control form-control-sm", "step": "0.01"}
            ),
            "pickup": forms.TextInput(attrs={"class": "form-control form-control-sm"}),
            "delivery": forms.TextInput(attrs={"class": "form-control form-control-sm"}),
            "pic": forms.TextInput(attrs={"class": "form-control form-control-sm"}),

            "payment_term": forms.Select(attrs={"class": "form-select form-select-sm"}),
            "currency": forms.Select(attrs={"class": "form-select form-select-sm"}),

            "remarks_internal": forms.Textarea(
                attrs={"class": "form-control form-control-sm", "rows": 3}
            ),

            "is_tax": forms.CheckboxInput(attrs={"class": "form-check-input"}),

            # angka: text + rata kanan
            "total_amount": forms.TextInput(
                attrs={"class": "form-control form-control-sm text-end"}
            ),
            "tax_amount": forms.TextInput(
                attrs={
                    "class": "form-control form-control-sm text-end",
                    "readonly": "readonly",
                    "tabindex": "-1",
                }
            ),
            "grand_total": forms.TextInput(
                attrs={"class": "form-control form-control-sm text-end"}
            ),

            # kurs & total IDR
            "kurs_idr": forms.TextInput(
                attrs={"class": "form-control form-control-sm text-end"}
            ),
            "total_in_idr": forms.TextInput(
                attrs={
                    "class": "form-control form-control-sm text-end",
                    "readonly": "readonly",
                    "tabindex": "-1",
                }
            ),
        }

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        # pakai proxy Customer biar dropdown cuma role customer
        if "customer" in self.fields:
            self.fields["customer"].queryset = Customer.objects.all().order_by("name")

        # styling massal (kalau ada field yang belum diset widget-nya)
        for name, field in self.fields.items():
            w = field.widget
            if isinstance(w, forms.CheckboxInput):
                w.attrs.setdefault("class", "form-check-input")
            elif isinstance(w, forms.Select):
                w.attrs.setdefault("class", "form-select form-select-sm")
            else:
                if "form-control" not in w.attrs.get("class", ""):
                    css = "form-control form-control-sm"
                    if name in ("total_amount", "tax_amount", "grand_total",
                                "kurs_idr", "total_in_idr"):
                        css += " text-end"
                    w.attrs.setdefault("class", css)

        # default angka "0,00" untuk form baru (create)
        if not self.instance.pk and not self.is_bound:
            self.initial.setdefault("total_amount", "0,00")
            self.initial.setdefault("tax_amount", "0,00")
            self.initial.setdefault("grand_total", "0,00")
            self.initial.setdefault("kurs_idr", "0,00")
            self.initial.setdefault("total_in_idr", "0,00")

    # ============================ helper angka ============================

    def _parse_id_decimal(self, value_str, field_label="angka"):
        """
        Ubah string format Indonesia '1.000.000,00' -> Decimal('1000000.00')
        Juga aman kalau input '2000000.00' (tanpa koma, dari DB).
        """
        if value_str in (None, ""):
            return Decimal("0")
        s = str(value_str).strip()
        s = s.replace(" ", "")

        # kalau mengandung koma → anggap format Indonesia
        if "," in s:
            s = s.replace(".", "").replace(",", ".")
        # kalau tidak ada koma → biarkan, '.' dianggap desimal biasa

        try:
            return Decimal(s)
        except InvalidOperation:
            raise forms.ValidationError(
                f"Format {field_label} tidak valid. Gunakan contoh: 1.000,00"
            )

    # ============================ clean utama ============================

    def clean(self):
        cleaned = super().clean()

        # 1) TOTAL / TAX / GRAND TOTAL
        total_raw = cleaned.get("total_amount") or ""
        is_tax = cleaned.get("is_tax") or False

        total = self._parse_id_decimal(total_raw, "Total Amount")

        if is_tax:
            tax = (total * Decimal("0.011")).quantize(Decimal("0.01"))
        else:
            tax = Decimal("0.00")

        grand = (total + tax).quantize(Decimal("0.01"))

        cleaned["total_amount"] = total
        cleaned["tax_amount"] = tax
        cleaned["grand_total"] = grand

        # 2) KURS & TOTAL DALAM IDR
        currency = cleaned.get("currency")
        kurs_raw = cleaned.get("kurs_idr") or ""

        if currency and getattr(currency, "code", "").upper() != "IDR":
            kurs = self._parse_id_decimal(kurs_raw, "Kurs IDR")
            if kurs <= 0:
                raise forms.ValidationError("Kurs IDR harus lebih besar dari 0.")
        else:
            # kalau IDR → kurs 1, dan total_in_idr = grand_total
            kurs = Decimal("1.00")

        total_in_idr = (grand * kurs).quantize(Decimal("0.01"))

        cleaned["kurs_idr"] = kurs
        cleaned["total_in_idr"] = total_in_idr

        return cleaned