from django.db import connection
from common.cash_bank_utils import get_current_balance
from common.utils import calculate_financial_year
def transfer_pending_counts(request):
"""
Calculates pending counts for the Material Transfer system sidebar badges.
Rules:
1. Material Request: Count shown ONLY to the Supplying Branch in 'Material Outward'.
2. Material Outward: Count shown ONLY to the Receiving Branch in 'Material Inward'.
"""
if 'user_id' not in request.session:
return {}
branch_id = request.session.get('branch_id')
# 1. Pending Transfer Outward (Requests RECEIVED BY this branch FROM others)
# Validation: SUM(request_qty) > SUM(outward_qty) for each request
pending_transfer_out_count = 0
with connection.cursor() as cursor:
cursor.execute("""
SELECT COUNT(*) FROM (
SELECT r.id
FROM tm_material_po r
INNER JOIN tx_material_po ri ON r.id = ri.tm_po_id
LEFT JOIN (
SELECT material_po_id,
SUM(CASE WHEN status = 1 AND is_active = 1 THEN quantity ELSE 0 END) as outward_qty
FROM tx_material_out
WHERE status = 1 AND is_active = 1
GROUP BY material_po_id
) o ON r.id = o.material_po_id
WHERE r.supply_branch_id = %s
AND r.status = 1 AND r.is_active = 1
AND ri.status = 1 AND ri.is_active = 1
AND r.id NOT IN (
SELECT DISTINCT txo.material_po_id
FROM tx_material_out txo
INNER JOIN tm_material_out tmo ON txo.tm_material_id = tmo.id
WHERE tmo.outward_status = 'rejected'
AND txo.material_po_id IS NOT NULL
)
GROUP BY r.id
HAVING SUM(ri.quantity) > COALESCE(MAX(o.outward_qty), 0)
) as pending
""", [branch_id])
row = cursor.fetchone()
pending_transfer_out_count = row[0] if row else 0
# 2. Pending Transfer Inward (Outwards SENT TO this branch FROM others)
# Validation: SUM(outward_qty) > SUM(inward_qty) for each outward
pending_transfer_in_count = 0
with connection.cursor() as cursor:
cursor.execute("""
SELECT COUNT(*) FROM (
SELECT o.id
FROM tm_material_out o
INNER JOIN tx_material_out oi ON o.id = oi.tm_material_id
LEFT JOIN (
SELECT material_outward_id,
SUM(CASE WHEN status = 1 AND is_active = 1 THEN quantity ELSE 0 END) as inward_qty
FROM tx_material_in
WHERE status = 1 AND is_active = 1
GROUP BY material_outward_id
) i ON o.id = i.material_outward_id
WHERE o.supply_branch_id = %s
AND o.status = 1 AND o.is_active = 1
AND oi.status = 1 AND oi.is_active = 1
AND o.outward_status != 'rejected'
GROUP BY o.id
HAVING SUM(oi.quantity) > COALESCE(MAX(i.inward_qty), 0)
) as pending
""", [branch_id])
row = cursor.fetchone()
pending_transfer_in_count = row[0] if row else 0
# User rule: Requesting branch does NOT see a count on their own request module
pending_transfer_req_count = 0
return {
'pending_transfer_req_count': pending_transfer_req_count,
'pending_transfer_out_count': pending_transfer_out_count,
'pending_transfer_in_count': pending_transfer_in_count,
'total_pending_transfer': pending_transfer_out_count + pending_transfer_in_count
}
def balance_marquee_processor(request):
"""
Generates balance marquee HTML for store users.
Shows cash balance and bank-wise balances in a scrolling marquee.
"""
if 'user_id' not in request.session:
return {}
user_type = request.session.get('user_type', '')
# Show for store and wholesale users
if user_type not in ('stores', 'wholesale', 'wholesale_customer'):
return {'balance_marquee': ''}
branch_id = request.session.get('branch_id')
fyf_name = request.session.get('fyf')
if not branch_id or not fyf_name:
return {'balance_marquee': ''}
try:
financial_year = calculate_financial_year(fyf_name)
balance_data = get_current_balance(branch_id, financial_year)
# Build marquee HTML
# Build marquee HTML
marquee_parts = []
# Common styles
# Common styles
label_style = "color: #000; font-weight: 700;"
value_style = "color: #dc3545; font-weight: 600;"
# Add cash balance
cash_balance = balance_data.get('cash_balance', 0)
marquee_parts.append(
f''
f'Cash: '
f'₹{cash_balance:.2f}'
f''
)
# Add bank balances
bank_list = balance_data.get('bank_wise', [])
for bank in bank_list:
bank_name = bank.get('bank_name', 'Unknown')
bank_balance = bank.get('balance', 0)
marquee_parts.append(
f''
f'{bank_name}: '
f'₹{bank_balance:.2f}'
f''
)
# Join with separator
marquee_html = ' | '.join(marquee_parts)
return {'balance_marquee': marquee_html}
except Exception as e:
# Silently fail and return empty marquee
return {'balance_marquee': ''}
def wholesale_internal_count(request):
"""
Calculates pending counts for wholesale internal sales (B2B Boys).
Displayed in the Wholesale sidebar and Purchase Inward submenu.
"""
if 'user_id' not in request.session:
return {}
branch_id = request.session.get('branch_id')
print('branch_id', branch_id)
if not branch_id:
return {}
with connection.cursor() as cursor:
# Fetch the count from sales_order_table (tm_sales)
# Filter: sales_channel = wholesale_internal
# Match customer_id with branch.id
# Apply branch filters: is_wholesale = 1, store_type = wholesale
cursor.execute("""
SELECT COUNT(s.id)
FROM tm_sales s
INNER JOIN branches b ON s.customer_id = b.id
WHERE s.sales_channel = 'wholesale_internal'
AND b.is_wholesale = 1
AND b.store_type = 'wholesale'
AND s.customer_id = %s
AND s.status = 1 AND s.is_active = 1
AND s.sales_status = 'pending'
""", [branch_id])
row = cursor.fetchone()
wholesale_internal_count = row[0] if row else 0
return {
'wholesale_internal_count': wholesale_internal_count
}
def sales_request_pending_counts(request):
"""
Calculates pending counts for Sales Requests (Wholesale POs)
where the current branch is the supply branch.
Follows the pattern of transfer_pending_counts.
"""
if 'user_id' not in request.session:
return {}
branch_id = request.session.get('branch_id')
fyf_name = request.session.get('fyf')
financial_year = calculate_financial_year(fyf_name) if fyf_name else None
pending_sales_request_count = 0
if branch_id and financial_year:
with connection.cursor() as cursor:
cursor.execute("""
SELECT COUNT(*)
FROM tm_wholesale_po
WHERE supply_branch_id = %s
AND po_status = 'pending'
AND status = 1
AND is_active = 1
AND current_fy = %s
""", [branch_id, financial_year])
row = cursor.fetchone()
pending_sales_request_count = row[0] if row else 0
return {
'pending_sales_request_count': pending_sales_request_count,
'total_pending_sales': pending_sales_request_count # Currently only Sales Request contributes to this
}