diff --git a/invoice_az/api.py b/invoice_az/api.py index a6669d8..3040a10 100644 --- a/invoice_az/api.py +++ b/invoice_az/api.py @@ -12,7 +12,7 @@ import time # Импорты из модуля аутентификации from invoice_az.auth import record_etaxes_activity, get_default_asan_login from invoice_az import background_tasks -from .utils import resolve_customer_group +from .utils import resolve_customer_group, exceeds_one_year from .sales_api import get_sales_invoices @@ -4389,7 +4389,7 @@ def import_bulk_purchase_invoices(invoice_ids, token, warehouse=None): user=user, bg_job_id=job_id, queue="default", - timeout=1200, + timeout=7200, ) return {"success": True, "enqueued": True, "total": len(invoice_ids), "job_id": job_id} @@ -4530,6 +4530,8 @@ def start_reference_data_loading_bg(date_from, date_to, load_items=0, return {"success": False, "message": "Invalid date range."} if df > dt: return {"success": False, "message": "Date From cannot be later than Date To."} + if exceeds_one_year(date_from, date_to): + return {"success": False, "message": "Date range cannot exceed one year."} date_from_str = df.strftime("%d-%m-%Y 00:00") date_to_str = dt.strftime("%d-%m-%Y 23:59") @@ -4750,6 +4752,8 @@ def start_items_loading_bg(date_from, date_to): record_etaxes_activity() if not date_from or not date_to: return {"success": False, "message": "Date range required."} + if exceeds_one_year(date_from, date_to): + return {"success": False, "message": "Date range cannot exceed one year."} job_id = background_tasks.new_job_id() frappe.enqueue( _process_entity_loading, @@ -4766,6 +4770,8 @@ def start_units_loading_bg(date_from, date_to): record_etaxes_activity() if not date_from or not date_to: return {"success": False, "message": "Date range required."} + if exceeds_one_year(date_from, date_to): + return {"success": False, "message": "Date range cannot exceed one year."} job_id = background_tasks.new_job_id() frappe.enqueue( _process_entity_loading, diff --git a/invoice_az/client/e_taxes_items_list.js b/invoice_az/client/e_taxes_items_list.js index 99826d7..17d3e70 100644 --- a/invoice_az/client/e_taxes_items_list.js +++ b/invoice_az/client/e_taxes_items_list.js @@ -688,6 +688,11 @@ function show_items_filter_dialog() { frappe.msgprint(__('To Date cannot be later than today')); return; } + + if (toDateMoment.diff(fromDateMoment, 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } d.hide(); diff --git a/invoice_az/client/e_taxes_unit_list.js b/invoice_az/client/e_taxes_unit_list.js index 6cfe378..126cb6d 100644 --- a/invoice_az/client/e_taxes_unit_list.js +++ b/invoice_az/client/e_taxes_unit_list.js @@ -648,6 +648,11 @@ function show_units_filter_dialog() { frappe.msgprint(__('To Date cannot be later than today')); return; } + + if (toDateMoment.diff(fromDateMoment, 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } d.hide(); diff --git a/invoice_az/client/journal_entry.js b/invoice_az/client/journal_entry.js index 3ba6a25..05a9ea6 100644 --- a/invoice_az/client/journal_entry.js +++ b/invoice_az/client/journal_entry.js @@ -925,6 +925,11 @@ VATETaxes.import = { return; } + if (toDateMoment.diff(fromDateMoment, 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } + if (fromDateMoment.isAfter(toDateMoment)) { frappe.msgprint(__('From Date cannot be later than To Date')); return; diff --git a/invoice_az/client/purchase_order.js b/invoice_az/client/purchase_order.js index 81742f6..fb6b6ec 100644 --- a/invoice_az/client/purchase_order.js +++ b/invoice_az/client/purchase_order.js @@ -901,6 +901,11 @@ ETaxes.import = { frappe.msgprint(__('To Date cannot be later than today')); return; } + + if (toDateMoment.diff(fromDateMoment, 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } if (!values.warehouse) { frappe.msgprint({ diff --git a/invoice_az/client/sales_invoice.js b/invoice_az/client/sales_invoice.js index 3c78df8..ee9fbf6 100644 --- a/invoice_az/client/sales_invoice.js +++ b/invoice_az/client/sales_invoice.js @@ -920,6 +920,11 @@ ETaxes.import = { frappe.msgprint(__('To Date cannot be later than today')); return; } + + if (toDateMoment.diff(fromDateMoment, 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } if (!values.warehouse) { frappe.msgprint({ diff --git a/invoice_az/client/sales_order.js b/invoice_az/client/sales_order.js index 5573172..a430b3a 100644 --- a/invoice_az/client/sales_order.js +++ b/invoice_az/client/sales_order.js @@ -902,6 +902,11 @@ SalesETaxes.import = { frappe.msgprint(__('To Date cannot be later than today')); return; } + + if (toDateMoment.diff(fromDateMoment, 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } if (!values.warehouse) { frappe.msgprint({ diff --git a/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js b/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js index df7528d..8917b2a 100644 --- a/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js +++ b/invoice_az/invoice_az/doctype/e_taxes_settings/e_taxes_settings.js @@ -953,6 +953,11 @@ function show_load_dialog_with_summary(frm, summary) { frappe.msgprint(__('Date From cannot be later than Date To.')); return; } + + if (moment(values.date_to).diff(moment(values.date_from), 'days') > 366) { + frappe.msgprint(__('The date range cannot exceed one year.')); + return; + } // Show confirmation with selected types const selected_types = []; diff --git a/invoice_az/sales_api.py b/invoice_az/sales_api.py index c528e75..3d18120 100644 --- a/invoice_az/sales_api.py +++ b/invoice_az/sales_api.py @@ -899,7 +899,7 @@ def import_bulk_sales_invoices(invoice_ids, token, warehouse=None): user=user, bg_job_id=job_id, queue="default", - timeout=1200, + timeout=7200, ) return {"success": True, "enqueued": True, "total": len(invoice_ids), "job_id": job_id} diff --git a/invoice_az/utils.py b/invoice_az/utils.py index b8be92f..36d7cd0 100644 --- a/invoice_az/utils.py +++ b/invoice_az/utils.py @@ -1,6 +1,7 @@ # ======= SHARED HELPERS ======= import frappe +from datetime import datetime def resolve_customer_group(preferred=None): @@ -27,3 +28,27 @@ def resolve_territory(preferred=None): if preferred and frappe.db.exists("Territory", preferred): return preferred return None + + +def _parse_load_date(value): + """Parse the date formats the e-taxes loaders use ('YYYY-MM-DD' or + 'DD-MM-YYYY[ HH:MM]'). Returns a datetime.date or None.""" + if not value: + return None + token = str(value).strip().split(" ")[0] + for fmt in ("%Y-%m-%d", "%d-%m-%Y"): + try: + return datetime.strptime(token, fmt).date() + except ValueError: + continue + return None + + +def exceeds_one_year(date_from, date_to): + """True if [date_from, date_to] spans more than one year (366 days). + + Used to forbid loading more than a year of data in a single run — applies to + every e-taxes load (invoices, reference data, VAT) EXCEPT Company data.""" + df = _parse_load_date(date_from) + dt = _parse_load_date(date_to) + return bool(df and dt and (dt - df).days > 366) diff --git a/invoice_az/vat_api.py b/invoice_az/vat_api.py index ace7db8..eeefb68 100644 --- a/invoice_az/vat_api.py +++ b/invoice_az/vat_api.py @@ -8,7 +8,7 @@ import json from datetime import datetime from frappe.utils import now_datetime, getdate from invoice_az.auth import record_etaxes_activity, get_default_asan_login -from invoice_az.utils import resolve_customer_group +from invoice_az.utils import resolve_customer_group, exceeds_one_year from invoice_az import background_tasks # API Endpoint @@ -97,6 +97,9 @@ def get_vat_operations(token, date_from, date_to, max_count=200, offset=0): max_count = 200 offset = 0 + if offset == 0 and exceeds_one_year(date_from, date_to): + return {"error": "range_too_long", "message": "Date range cannot exceed one year."} + payload = { "accounts": None, "creationDateFrom": date_from, @@ -1147,7 +1150,7 @@ def import_bulk_vat_operations(operations_data, company=None, create_as_draft=0, user=user, bg_job_id=job_id, queue="default", - timeout=1200, + timeout=7200, ) return {"success": True, "enqueued": True, "total": len(operations_data), "job_id": job_id}