Unify accounting analysis on local SQLite mirror
This commit is contained in:
@@ -3,7 +3,7 @@ from __future__ import annotations
|
||||
import json
|
||||
from io import BytesIO
|
||||
from urllib.parse import urlencode
|
||||
from datetime import datetime
|
||||
from datetime import date, datetime
|
||||
|
||||
from fastapi import APIRouter, Form, Request
|
||||
from fastapi.responses import RedirectResponse, StreamingResponse
|
||||
@@ -24,6 +24,7 @@ from app.modules.accounting.opening_balance_service import (
|
||||
)
|
||||
from app.modules.accounting.ui import (
|
||||
_accounting_storage_payload,
|
||||
_financial_year_for_date,
|
||||
_find_visible_client,
|
||||
_node_online,
|
||||
_require_partner,
|
||||
@@ -118,6 +119,7 @@ def page(
|
||||
show_zero_new: int = 0,
|
||||
message: str = "",
|
||||
error: str = "",
|
||||
analysis_run_id: int = 0,
|
||||
):
|
||||
db = CommonSessionLocal()
|
||||
try:
|
||||
@@ -136,20 +138,35 @@ def page(
|
||||
tally = None
|
||||
companies = []
|
||||
command_error = error or ""
|
||||
today = date.today()
|
||||
current_fy = _financial_year_for_date(today)
|
||||
current_start = int(current_fy.split("-", 1)[0])
|
||||
previous_fy = f"{current_start-1}-{str(current_start)[-2:]}"
|
||||
previous_mirror_status = None
|
||||
current_mirror_status = None
|
||||
|
||||
if selected and node and online:
|
||||
try:
|
||||
response = request_agent_command(
|
||||
prev_response = request_agent_command(
|
||||
node.node_code,
|
||||
"phase6_status",
|
||||
_accounting_storage_payload(selected),
|
||||
"accounting_mirror_status",
|
||||
_accounting_storage_payload(selected, previous_fy),
|
||||
timeout_seconds=20,
|
||||
)
|
||||
if response.get("ok"):
|
||||
tally = (response.get("result") or {}).get("tally")
|
||||
companies = _company_rows(tally)
|
||||
else:
|
||||
command_error = str(response.get("error") or "Local Agent status failed.")
|
||||
curr_response = request_agent_command(
|
||||
node.node_code,
|
||||
"accounting_mirror_status",
|
||||
_accounting_storage_payload(selected, current_fy),
|
||||
timeout_seconds=20,
|
||||
)
|
||||
if prev_response.get("ok"):
|
||||
previous_mirror_status = prev_response.get("result") or {}
|
||||
if curr_response.get("ok"):
|
||||
current_mirror_status = curr_response.get("result") or {}
|
||||
if not prev_response.get("ok"):
|
||||
command_error = str(prev_response.get("error") or "Previous-year Accounting Mirror status failed.")
|
||||
elif not curr_response.get("ok"):
|
||||
command_error = str(curr_response.get("error") or "Current-year Accounting Mirror status failed.")
|
||||
except Exception as exc:
|
||||
command_error = str(exc)
|
||||
|
||||
@@ -467,8 +484,8 @@ def download_report(request: Request, run_id: int, client_id: int | None = None)
|
||||
def compare(
|
||||
request: Request,
|
||||
client_id: int = Form(...),
|
||||
previous_company_name: str = Form(...),
|
||||
current_company_name: str = Form(...),
|
||||
previous_fy: str = Form(...),
|
||||
current_fy: str = Form(...),
|
||||
csrf_token: str = Form(...),
|
||||
):
|
||||
validate_csrf(request, csrf_token)
|
||||
@@ -477,32 +494,30 @@ def compare(
|
||||
user, denied = _require_partner(request, db, "accounting.tally.sync_masters")
|
||||
if denied:
|
||||
return denied
|
||||
|
||||
client, _clients, scope = _find_visible_client(db, request, user, client_id)
|
||||
if not client:
|
||||
return _go(error="Client is not visible.")
|
||||
if previous_company_name.strip().casefold() == current_company_name.strip().casefold():
|
||||
raise ValueError(
|
||||
"Previous-year and current-year companies must be different in Phase 21A. "
|
||||
"This protects against using the current company's latest closing balance as the prior-year closing."
|
||||
)
|
||||
if previous_fy.strip() == current_fy.strip():
|
||||
raise ValueError("Previous-year and current-year mirrors must be different.")
|
||||
|
||||
node = get_active_storage_node_for_branch(db, scope.tenant_id, scope.branch_id)
|
||||
if not node or not _node_online(node):
|
||||
raise ValueError("ERP Local Agent is offline for the active branch.")
|
||||
|
||||
previous_payload = _accounting_storage_payload(client, previous_fy)
|
||||
current_payload = _accounting_storage_payload(client, current_fy)
|
||||
response = request_agent_command(
|
||||
node.node_code,
|
||||
"accounting_opening_balance_snapshot",
|
||||
"accounting_opening_balance_mirror_snapshot",
|
||||
{
|
||||
**_accounting_storage_payload(client),
|
||||
"previous_company_name": previous_company_name,
|
||||
"current_company_name": current_company_name,
|
||||
**current_payload,
|
||||
"previous_accounting_relative_dir": previous_payload["accounting_relative_dir"],
|
||||
"current_accounting_relative_dir": current_payload["accounting_relative_dir"],
|
||||
},
|
||||
timeout_seconds=180,
|
||||
)
|
||||
if not response.get("ok"):
|
||||
raise RuntimeError(str(response.get("error") or "Opening balance snapshot failed."))
|
||||
raise RuntimeError(str(response.get("error") or "Opening balance mirror comparison failed."))
|
||||
|
||||
result = response.get("result") or {}
|
||||
run = create_comparison_run(
|
||||
@@ -515,10 +530,63 @@ def compare(
|
||||
current_masters=result.get("current_masters") or {},
|
||||
user_id=user.id,
|
||||
)
|
||||
|
||||
ledger_rows = ledger_items(db, run_id=run.id)
|
||||
stock_rows = stock_items(db, run_id=run.id)
|
||||
try:
|
||||
saved = request_agent_command(
|
||||
node.node_code,
|
||||
"accounting_analysis_save",
|
||||
{
|
||||
**current_payload,
|
||||
"analysis_type": "OPENING_BALANCE_REVIEW",
|
||||
"financial_year": current_fy,
|
||||
"period_from": previous_fy,
|
||||
"period_to": current_fy,
|
||||
"company_guid": run.current_company_guid,
|
||||
"company_name": run.current_company_name,
|
||||
"requested_by_user_id": int(user.id),
|
||||
"source_mirror_path": str(result.get("current_mirror_path") or ""),
|
||||
"parameters": {"previous_fy": previous_fy, "current_fy": current_fy, "erp_run_id": run.id},
|
||||
"summary": json.loads(run.summary_json or "{}"),
|
||||
"result": {
|
||||
"erp_run_id": run.id,
|
||||
"previous_company": result.get("previous_company") or {},
|
||||
"current_company": result.get("current_company") or {},
|
||||
"ledgers": [
|
||||
{
|
||||
"previous_name": x.previous_name,
|
||||
"current_name": x.current_name,
|
||||
"previous_closing_balance": x.previous_closing_balance,
|
||||
"current_opening_balance": x.current_opening_balance,
|
||||
"difference": x.difference,
|
||||
"status": x.status,
|
||||
} for x in ledger_rows
|
||||
],
|
||||
"stock_items": [
|
||||
{
|
||||
"previous_name": x.previous_name,
|
||||
"current_name": x.current_name,
|
||||
"previous_closing_qty": x.previous_closing_qty,
|
||||
"current_opening_qty": x.current_opening_qty,
|
||||
"previous_closing_value": x.previous_closing_value,
|
||||
"current_opening_value": x.current_opening_value,
|
||||
"status": x.status,
|
||||
} for x in stock_rows
|
||||
],
|
||||
},
|
||||
},
|
||||
timeout_seconds=30,
|
||||
)
|
||||
analysis_run_id = int(((saved.get("result") or {}).get("analysis_run_id") or 0)) if saved.get("ok") else 0
|
||||
except Exception:
|
||||
analysis_run_id = 0
|
||||
|
||||
return _go(
|
||||
client.id,
|
||||
run_id=run.id,
|
||||
tab="summary",
|
||||
analysis_run_id=analysis_run_id,
|
||||
message=(
|
||||
f"Opening balance comparison #{run.id} completed: "
|
||||
f"{run.ledger_count} ledger row(s), {run.stock_item_count} stock row(s)."
|
||||
|
||||
Reference in New Issue
Block a user