|
@@ -157,59 +157,55 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
prepaid_sol_lines = SaleOrderLine.search(domain)
|
|
prepaid_sol_lines = SaleOrderLine.search(domain)
|
|
|
|
|
|
|
|
# NEW LOGIC: Calculate hours based on invoice payment status
|
|
# NEW LOGIC: Calculate hours based on invoice payment status
|
|
|
- # - paid_hours: hours from lines with fully PAID invoices
|
|
|
|
|
- # - unpaid_hours: hours from lines with UNPAID/partial invoices
|
|
|
|
|
- # This replaces the old _is_order_paid check
|
|
|
|
|
|
|
+ # - paid_hours: hours from PAID invoices only (invoice line qty)
|
|
|
|
|
+ # - unpaid_invoice_hours: hours from UNPAID invoices
|
|
|
|
|
+ # - uninvoiced_hours: hours sold but not yet invoiced
|
|
|
|
|
|
|
|
paid_hours = 0.0
|
|
paid_hours = 0.0
|
|
|
- unpaid_hours = 0.0
|
|
|
|
|
|
|
+ unpaid_invoice_hours = 0.0
|
|
|
|
|
+ uninvoiced_hours = 0.0
|
|
|
highest_price = 0.0
|
|
highest_price = 0.0
|
|
|
|
|
|
|
|
for line in prepaid_sol_lines:
|
|
for line in prepaid_sol_lines:
|
|
|
try:
|
|
try:
|
|
|
- # Get remaining hours for this line
|
|
|
|
|
- remaining = line.remaining_hours or 0.0
|
|
|
|
|
- if remaining <= 0:
|
|
|
|
|
|
|
+ # Get quantities for this line
|
|
|
|
|
+ qty_sold = line.product_uom_qty or 0.0
|
|
|
|
|
+ qty_invoiced = line.qty_invoiced or 0.0
|
|
|
|
|
+ qty_delivered = line.qty_delivered or 0.0
|
|
|
|
|
+
|
|
|
|
|
+ if qty_sold <= 0:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
# Track highest price unit
|
|
# Track highest price unit
|
|
|
if line.price_unit > highest_price:
|
|
if line.price_unit > highest_price:
|
|
|
highest_price = line.price_unit
|
|
highest_price = line.price_unit
|
|
|
|
|
|
|
|
- # Check invoice payment status for this line
|
|
|
|
|
- # A line can have multiple invoice lines, check all of them
|
|
|
|
|
|
|
+ # Calculate uninvoiced hours (sold but not yet invoiced)
|
|
|
|
|
+ qty_uninvoiced = max(0.0, qty_sold - qty_invoiced)
|
|
|
|
|
+ uninvoiced_hours += qty_uninvoiced
|
|
|
|
|
+
|
|
|
|
|
+ # For invoiced portion, check payment status per invoice
|
|
|
invoice_lines = line.invoice_lines.sudo()
|
|
invoice_lines = line.invoice_lines.sudo()
|
|
|
|
|
|
|
|
if not invoice_lines:
|
|
if not invoice_lines:
|
|
|
- # No invoices yet - consider as unpaid credit
|
|
|
|
|
- unpaid_hours += max(0.0, remaining)
|
|
|
|
|
|
|
+ # No invoices - all goes to uninvoiced (already counted above)
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- # Get unique invoices for this line
|
|
|
|
|
- invoices = invoice_lines.mapped('move_id').filtered(
|
|
|
|
|
- lambda m: m.move_type == 'out_invoice' and m.state == 'posted'
|
|
|
|
|
- )
|
|
|
|
|
-
|
|
|
|
|
- if not invoices:
|
|
|
|
|
- # No posted invoices - consider as unpaid credit
|
|
|
|
|
- unpaid_hours += max(0.0, remaining)
|
|
|
|
|
- continue
|
|
|
|
|
-
|
|
|
|
|
- # Check if ALL invoices are paid
|
|
|
|
|
- # payment_state: 'not_paid', 'partial', 'paid', 'in_payment', 'reversed'
|
|
|
|
|
- all_paid = all(inv.payment_state == 'paid' for inv in invoices)
|
|
|
|
|
- any_paid = any(inv.payment_state == 'paid' for inv in invoices)
|
|
|
|
|
-
|
|
|
|
|
- if all_paid:
|
|
|
|
|
- # All invoices paid - hours are available
|
|
|
|
|
- paid_hours += max(0.0, remaining)
|
|
|
|
|
- elif any_paid:
|
|
|
|
|
- # Partial payment - split proportionally
|
|
|
|
|
- # For simplicity, count as unpaid (conservative approach)
|
|
|
|
|
- unpaid_hours += max(0.0, remaining)
|
|
|
|
|
- else:
|
|
|
|
|
- # No paid invoices - count as credit
|
|
|
|
|
- unpaid_hours += max(0.0, remaining)
|
|
|
|
|
|
|
+ # Process each invoice line
|
|
|
|
|
+ for inv_line in invoice_lines:
|
|
|
|
|
+ inv = inv_line.move_id
|
|
|
|
|
+ # Only count posted customer invoices
|
|
|
|
|
+ if inv.move_type != 'out_invoice' or inv.state != 'posted':
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ inv_qty = inv_line.quantity or 0.0
|
|
|
|
|
+
|
|
|
|
|
+ if inv.payment_state == 'paid':
|
|
|
|
|
+ # Paid invoice - hours are available
|
|
|
|
|
+ paid_hours += inv_qty
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Not paid (not_paid, partial, in_payment, etc.)
|
|
|
|
|
+ unpaid_invoice_hours += inv_qty
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
_logger.debug(
|
|
_logger.debug(
|
|
@@ -286,11 +282,11 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
if highest_price > 0 and credit_available > 0:
|
|
if highest_price > 0 and credit_available > 0:
|
|
|
credit_from_limit = credit_available / highest_price
|
|
credit_from_limit = credit_available / highest_price
|
|
|
|
|
|
|
|
- # NEW: Credit hours = unpaid invoice hours + credit limit hours
|
|
|
|
|
- credit_hours = unpaid_hours + credit_from_limit
|
|
|
|
|
|
|
+ # NEW: Credit hours = uninvoiced hours + unpaid invoice hours + credit limit hours
|
|
|
|
|
+ credit_hours = uninvoiced_hours + unpaid_invoice_hours + credit_from_limit
|
|
|
|
|
|
|
|
- # Available hours = paid invoice hours only
|
|
|
|
|
- prepaid_hours = paid_hours
|
|
|
|
|
|
|
+ # Available hours = paid invoice hours only (minus used hours)
|
|
|
|
|
+ prepaid_hours = max(0.0, paid_hours - hours_used)
|
|
|
|
|
|
|
|
total_available = prepaid_hours + credit_hours
|
|
total_available = prepaid_hours + credit_hours
|
|
|
|
|
|