|
|
@@ -156,44 +156,70 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
# Search for prepaid lines following Odoo's standard procedure
|
|
|
prepaid_sol_lines = SaleOrderLine.search(domain)
|
|
|
|
|
|
- # Filter lines from orders that have received payment
|
|
|
- # Only consider hours from orders with paid invoices
|
|
|
- helpdesk_team_model = request.env["helpdesk.team"]
|
|
|
+ # 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 = 0.0
|
|
|
+ unpaid_hours = 0.0
|
|
|
+ highest_price = 0.0
|
|
|
|
|
|
- # Filter lines from orders that have received payment
|
|
|
- # Use explicit loop to handle exceptions properly
|
|
|
- paid_prepaid_lines = request.env["sale.order.line"].sudo()
|
|
|
for line in prepaid_sol_lines:
|
|
|
try:
|
|
|
- if helpdesk_team_model._is_order_paid(line.order_id):
|
|
|
- paid_prepaid_lines |= line
|
|
|
+ # Get remaining hours for this line
|
|
|
+ remaining = line.remaining_hours or 0.0
|
|
|
+ if remaining <= 0:
|
|
|
+ continue
|
|
|
+
|
|
|
+ # Track highest price unit
|
|
|
+ if line.price_unit > highest_price:
|
|
|
+ highest_price = line.price_unit
|
|
|
+
|
|
|
+ # Check invoice payment status for this line
|
|
|
+ # A line can have multiple invoice lines, check all of them
|
|
|
+ invoice_lines = line.invoice_lines.sudo()
|
|
|
+
|
|
|
+ if not invoice_lines:
|
|
|
+ # No invoices yet - consider as unpaid credit
|
|
|
+ unpaid_hours += max(0.0, remaining)
|
|
|
+ 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)
|
|
|
+
|
|
|
except Exception as e:
|
|
|
- # Log exception only in debug mode
|
|
|
_logger.debug(
|
|
|
- "Error checking payment for line %s, order %s: %s",
|
|
|
+ "Error calculating hours for line %s: %s",
|
|
|
line.id,
|
|
|
- line.order_id.id,
|
|
|
str(e),
|
|
|
exc_info=True
|
|
|
)
|
|
|
|
|
|
- # Calculate prepaid hours using Odoo's remaining_hours field
|
|
|
- # This is the correct way as it handles UOM conversion automatically
|
|
|
- prepaid_hours = 0.0
|
|
|
- highest_price = 0.0
|
|
|
-
|
|
|
- for line in paid_prepaid_lines:
|
|
|
- # Use remaining_hours directly (already in hours, handles UOM conversion)
|
|
|
- # This is the field Odoo uses and calculates correctly
|
|
|
- remaining = line.remaining_hours or 0.0
|
|
|
- prepaid_hours += max(0.0, remaining)
|
|
|
-
|
|
|
- # Track highest price unit
|
|
|
- if line.price_unit > highest_price:
|
|
|
- highest_price = line.price_unit
|
|
|
-
|
|
|
- # If no paid lines with price, try to get price from all prepaid lines (historical)
|
|
|
- # This is needed to calculate credit_hours even if there are no paid lines currently
|
|
|
+ # If no lines with price, try to get price from all prepaid lines (historical)
|
|
|
if highest_price == 0 and prepaid_sol_lines:
|
|
|
for line in prepaid_sol_lines:
|
|
|
if line.price_unit > highest_price:
|
|
|
@@ -205,7 +231,6 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
# Use the same extended partner domain
|
|
|
base_hours_used_domain = [
|
|
|
("company_id", "=", company.id),
|
|
|
- # ("order_partner_id", "child_of", partner.id),
|
|
|
("state", "in", ["sale", "done"]),
|
|
|
]
|
|
|
|
|
|
@@ -233,29 +258,10 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
|
|
|
all_prepaid_lines = SaleOrderLine.search(hours_used_domain)
|
|
|
|
|
|
- # Filter lines from orders that have received payment
|
|
|
- # Only consider hours used from orders with paid invoices
|
|
|
- # Use explicit loop to handle exceptions properly
|
|
|
- paid_all_prepaid_lines = request.env["sale.order.line"].sudo()
|
|
|
- for line in all_prepaid_lines:
|
|
|
- try:
|
|
|
- if helpdesk_team_model._is_order_paid(line.order_id):
|
|
|
- paid_all_prepaid_lines |= line
|
|
|
- except Exception as e:
|
|
|
- # Log exception only in debug mode
|
|
|
- _logger.debug(
|
|
|
- "Error checking payment for line %s, order %s: %s",
|
|
|
- line.id,
|
|
|
- line.order_id.id,
|
|
|
- str(e),
|
|
|
- exc_info=True
|
|
|
- )
|
|
|
-
|
|
|
hours_used = 0.0
|
|
|
|
|
|
- for line in paid_all_prepaid_lines:
|
|
|
+ for line in all_prepaid_lines:
|
|
|
# Calculate hours used: qty_delivered converted to hours
|
|
|
- # Use the same UOM conversion that Odoo uses
|
|
|
qty_delivered = line.qty_delivered or 0.0
|
|
|
if qty_delivered > 0:
|
|
|
qty_delivered_hours = (
|
|
|
@@ -266,12 +272,11 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
)
|
|
|
hours_used += qty_delivered_hours
|
|
|
|
|
|
- # Calculate credit hours
|
|
|
- credit_hours = 0.0
|
|
|
+ # Calculate credit hours from partner credit limit
|
|
|
+ credit_from_limit = 0.0
|
|
|
credit_available = 0.0
|
|
|
|
|
|
# Check if credit limit is configured
|
|
|
- # Use sudo to access credit fields which may have restricted access
|
|
|
partner_sudo = partner.sudo()
|
|
|
if company.account_use_credit_limit and partner_sudo.credit_limit > 0:
|
|
|
credit_used = partner_sudo.credit or 0.0
|
|
|
@@ -279,12 +284,14 @@ class WebsiteHelpdeskHours(http.Controller):
|
|
|
|
|
|
# Convert credit to hours using highest price
|
|
|
if highest_price > 0 and credit_available > 0:
|
|
|
- credit_hours = credit_available / highest_price
|
|
|
- elif highest_price == 0 and credit_available > 0:
|
|
|
- # If no hours sold yet, we can't calculate credit hours
|
|
|
- # But we still show the credit available
|
|
|
- credit_hours = 0.0
|
|
|
+ credit_from_limit = credit_available / highest_price
|
|
|
|
|
|
+ # NEW: Credit hours = unpaid invoice hours + credit limit hours
|
|
|
+ credit_hours = unpaid_hours + credit_from_limit
|
|
|
+
|
|
|
+ # Available hours = paid invoice hours only
|
|
|
+ prepaid_hours = paid_hours
|
|
|
+
|
|
|
total_available = prepaid_hours + credit_hours
|
|
|
|
|
|
return {
|