feat(loads): 2h import timeout + forbid loading more than one year
- Raise purchase/sales/VAT bulk-import job timeout 1200s -> 7200s (2h), matching the reference/items/units loaders. AMAS and Company are unchanged. - Add utils.exceeds_one_year() (parses both 'YYYY-MM-DD' and 'DD-MM-YYYY HH:MM') and reject date ranges spanning over one year on every e-taxes load except Company data: server-side in the reference/items/units/VAT entry points, and client-side in all 7 load dialogs for immediate feedback. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
93c6f13325
commit
c29ac5bd1b
|
|
@ -12,7 +12,7 @@ import time
|
||||||
# Импорты из модуля аутентификации
|
# Импорты из модуля аутентификации
|
||||||
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
||||||
from invoice_az import background_tasks
|
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
|
from .sales_api import get_sales_invoices
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -4389,7 +4389,7 @@ def import_bulk_purchase_invoices(invoice_ids, token, warehouse=None):
|
||||||
user=user,
|
user=user,
|
||||||
bg_job_id=job_id,
|
bg_job_id=job_id,
|
||||||
queue="default",
|
queue="default",
|
||||||
timeout=1200,
|
timeout=7200,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"success": True, "enqueued": True, "total": len(invoice_ids), "job_id": job_id}
|
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."}
|
return {"success": False, "message": "Invalid date range."}
|
||||||
if df > dt:
|
if df > dt:
|
||||||
return {"success": False, "message": "Date From cannot be later than Date To."}
|
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_from_str = df.strftime("%d-%m-%Y 00:00")
|
||||||
date_to_str = dt.strftime("%d-%m-%Y 23:59")
|
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()
|
record_etaxes_activity()
|
||||||
if not date_from or not date_to:
|
if not date_from or not date_to:
|
||||||
return {"success": False, "message": "Date range required."}
|
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()
|
job_id = background_tasks.new_job_id()
|
||||||
frappe.enqueue(
|
frappe.enqueue(
|
||||||
_process_entity_loading,
|
_process_entity_loading,
|
||||||
|
|
@ -4766,6 +4770,8 @@ def start_units_loading_bg(date_from, date_to):
|
||||||
record_etaxes_activity()
|
record_etaxes_activity()
|
||||||
if not date_from or not date_to:
|
if not date_from or not date_to:
|
||||||
return {"success": False, "message": "Date range required."}
|
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()
|
job_id = background_tasks.new_job_id()
|
||||||
frappe.enqueue(
|
frappe.enqueue(
|
||||||
_process_entity_loading,
|
_process_entity_loading,
|
||||||
|
|
|
||||||
|
|
@ -689,6 +689,11 @@ function show_items_filter_dialog() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDateMoment.diff(fromDateMoment, 'days') > 366) {
|
||||||
|
frappe.msgprint(__('The date range cannot exceed one year.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
d.hide();
|
d.hide();
|
||||||
|
|
||||||
let fromDate = values.creationDateFrom ? moment(values.creationDateFrom).format('DD-MM-YYYY 00:00') : moment(startDate).format('DD-MM-YYYY 00:00');
|
let fromDate = values.creationDateFrom ? moment(values.creationDateFrom).format('DD-MM-YYYY 00:00') : moment(startDate).format('DD-MM-YYYY 00:00');
|
||||||
|
|
|
||||||
|
|
@ -649,6 +649,11 @@ function show_units_filter_dialog() {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDateMoment.diff(fromDateMoment, 'days') > 366) {
|
||||||
|
frappe.msgprint(__('The date range cannot exceed one year.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
d.hide();
|
d.hide();
|
||||||
|
|
||||||
let fromDate = values.creationDateFrom ? moment(values.creationDateFrom).format('DD-MM-YYYY 00:00') : moment(startDate).format('DD-MM-YYYY 00:00');
|
let fromDate = values.creationDateFrom ? moment(values.creationDateFrom).format('DD-MM-YYYY 00:00') : moment(startDate).format('DD-MM-YYYY 00:00');
|
||||||
|
|
|
||||||
|
|
@ -925,6 +925,11 @@ VATETaxes.import = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDateMoment.diff(fromDateMoment, 'days') > 366) {
|
||||||
|
frappe.msgprint(__('The date range cannot exceed one year.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (fromDateMoment.isAfter(toDateMoment)) {
|
if (fromDateMoment.isAfter(toDateMoment)) {
|
||||||
frappe.msgprint(__('From Date cannot be later than To Date'));
|
frappe.msgprint(__('From Date cannot be later than To Date'));
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -902,6 +902,11 @@ ETaxes.import = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDateMoment.diff(fromDateMoment, 'days') > 366) {
|
||||||
|
frappe.msgprint(__('The date range cannot exceed one year.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!values.warehouse) {
|
if (!values.warehouse) {
|
||||||
frappe.msgprint({
|
frappe.msgprint({
|
||||||
title: __('Warning'),
|
title: __('Warning'),
|
||||||
|
|
|
||||||
|
|
@ -921,6 +921,11 @@ ETaxes.import = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDateMoment.diff(fromDateMoment, 'days') > 366) {
|
||||||
|
frappe.msgprint(__('The date range cannot exceed one year.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!values.warehouse) {
|
if (!values.warehouse) {
|
||||||
frappe.msgprint({
|
frappe.msgprint({
|
||||||
title: __('Warning'),
|
title: __('Warning'),
|
||||||
|
|
|
||||||
|
|
@ -903,6 +903,11 @@ SalesETaxes.import = {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (toDateMoment.diff(fromDateMoment, 'days') > 366) {
|
||||||
|
frappe.msgprint(__('The date range cannot exceed one year.'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (!values.warehouse) {
|
if (!values.warehouse) {
|
||||||
frappe.msgprint({
|
frappe.msgprint({
|
||||||
title: __('Warning'),
|
title: __('Warning'),
|
||||||
|
|
|
||||||
|
|
@ -954,6 +954,11 @@ function show_load_dialog_with_summary(frm, summary) {
|
||||||
return;
|
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
|
// Show confirmation with selected types
|
||||||
const selected_types = [];
|
const selected_types = [];
|
||||||
if (values.load_items) selected_types.push(__('Items'));
|
if (values.load_items) selected_types.push(__('Items'));
|
||||||
|
|
|
||||||
|
|
@ -899,7 +899,7 @@ def import_bulk_sales_invoices(invoice_ids, token, warehouse=None):
|
||||||
user=user,
|
user=user,
|
||||||
bg_job_id=job_id,
|
bg_job_id=job_id,
|
||||||
queue="default",
|
queue="default",
|
||||||
timeout=1200,
|
timeout=7200,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"success": True, "enqueued": True, "total": len(invoice_ids), "job_id": job_id}
|
return {"success": True, "enqueued": True, "total": len(invoice_ids), "job_id": job_id}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
# ======= SHARED HELPERS =======
|
# ======= SHARED HELPERS =======
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
def resolve_customer_group(preferred=None):
|
def resolve_customer_group(preferred=None):
|
||||||
|
|
@ -27,3 +28,27 @@ def resolve_territory(preferred=None):
|
||||||
if preferred and frappe.db.exists("Territory", preferred):
|
if preferred and frappe.db.exists("Territory", preferred):
|
||||||
return preferred
|
return preferred
|
||||||
return None
|
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)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import json
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from frappe.utils import now_datetime, getdate
|
from frappe.utils import now_datetime, getdate
|
||||||
from invoice_az.auth import record_etaxes_activity, get_default_asan_login
|
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
|
from invoice_az import background_tasks
|
||||||
|
|
||||||
# API Endpoint
|
# API Endpoint
|
||||||
|
|
@ -97,6 +97,9 @@ def get_vat_operations(token, date_from, date_to, max_count=200, offset=0):
|
||||||
max_count = 200
|
max_count = 200
|
||||||
offset = 0
|
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 = {
|
payload = {
|
||||||
"accounts": None,
|
"accounts": None,
|
||||||
"creationDateFrom": date_from,
|
"creationDateFrom": date_from,
|
||||||
|
|
@ -1147,7 +1150,7 @@ def import_bulk_vat_operations(operations_data, company=None, create_as_draft=0,
|
||||||
user=user,
|
user=user,
|
||||||
bg_job_id=job_id,
|
bg_job_id=job_id,
|
||||||
queue="default",
|
queue="default",
|
||||||
timeout=1200,
|
timeout=7200,
|
||||||
)
|
)
|
||||||
|
|
||||||
return {"success": True, "enqueued": True, "total": len(operations_data), "job_id": job_id}
|
return {"success": True, "enqueued": True, "total": len(operations_data), "job_id": job_id}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue