瀏覽代碼

feat(helpdesk): refactor hours calc - available=paid invoices, credit=unpaid+limit

odoo 2 月之前
父節點
當前提交
85a7ba2474
共有 1 個文件被更改,包括 64 次插入57 次删除
  1. 64 57
      helpdesk_extras/controllers/website_helpdesk_hours.py

+ 64 - 57
helpdesk_extras/controllers/website_helpdesk_hours.py

@@ -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 {