feat: Fase 1-3 completas - precios proveedor, multi-sucursal, factura global
Fase 1: Lista de precios de proveedor - Tabla supplier_catalog_prices en master DB - Endpoints GET/POST/PUT/DELETE /supplier-catalog/prices - Upload CSV/Excel de precios de proveedor - Visualizacion de supplier_price en catalogo y POS Fase 2: Multi-sucursal completo - Migracion v4.0: inventory.branch_id=NULL, tabla inventory_stock - Campos fiscales en branches (RFC, regimen, CP, serie CFDI, certificados) - Trigger trg_update_inventory_stock para sincronizar stock por sucursal - Backend config_bp.py con CRUD de sucursales fiscales - Backend inventory_bp.py y pos_bp.py refactorizados para inventario compartido - Backend invoicing_bp.py usa datos fiscales de la sucursal de la venta - Frontend config.html/js con modal de sucursales expandido Fase 3: Factura global mensual - Migracion v4.1: tablas global_invoice_sales, sales.global_invoiced_at - build_global_invoice_xml() con InformacionGlobal SAT-compliant - Servicio global_invoice.py para agrupar ventas PUE <=000 - Endpoints POST/GET /global-invoice y /global-invoice/eligible-sales - Frontend invoicing.html/js con boton y modal de factura global
This commit is contained in:
@@ -13,15 +13,53 @@ config_bp = Blueprint('config', __name__, url_prefix='/pos/api/config')
|
||||
def list_branches():
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
cur.execute("SELECT id, name, address, phone, is_active FROM branches ORDER BY id")
|
||||
cur.execute("""
|
||||
SELECT id, name, address, phone, is_active, is_main,
|
||||
rfc, razon_social, regimen_fiscal, codigo_postal,
|
||||
serie_cfdi, folio_inicial, licencia_fiscal
|
||||
FROM branches ORDER BY id
|
||||
""")
|
||||
branches = []
|
||||
for r in cur.fetchall():
|
||||
branches.append({'id': r[0], 'name': r[1], 'address': r[2], 'phone': r[3], 'is_active': r[4]})
|
||||
branches.append({
|
||||
'id': r[0], 'name': r[1], 'address': r[2], 'phone': r[3],
|
||||
'is_active': r[4], 'is_main': r[5],
|
||||
'rfc': r[6], 'razon_social': r[7], 'regimen_fiscal': r[8],
|
||||
'codigo_postal': r[9], 'serie_cfdi': r[10],
|
||||
'folio_inicial': r[11], 'licencia_fiscal': r[12],
|
||||
})
|
||||
cur.close()
|
||||
conn.close()
|
||||
return jsonify({'data': branches})
|
||||
|
||||
|
||||
@config_bp.route('/branches/<int:branch_id>', methods=['GET'])
|
||||
@require_auth('config.view')
|
||||
def get_branch(branch_id):
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, name, address, phone, is_active, is_main,
|
||||
rfc, razon_social, regimen_fiscal, codigo_postal,
|
||||
serie_cfdi, folio_inicial, licencia_fiscal,
|
||||
certificado_pem, llave_pem
|
||||
FROM branches WHERE id = %s
|
||||
""", (branch_id,))
|
||||
r = cur.fetchone()
|
||||
cur.close()
|
||||
conn.close()
|
||||
if not r:
|
||||
return jsonify({'error': 'Branch not found'}), 404
|
||||
return jsonify({
|
||||
'id': r[0], 'name': r[1], 'address': r[2], 'phone': r[3],
|
||||
'is_active': r[4], 'is_main': r[5],
|
||||
'rfc': r[6], 'razon_social': r[7], 'regimen_fiscal': r[8],
|
||||
'codigo_postal': r[9], 'serie_cfdi': r[10],
|
||||
'folio_inicial': r[11], 'licencia_fiscal': r[12],
|
||||
'certificado_pem': r[13], 'llave_pem': r[14],
|
||||
})
|
||||
|
||||
|
||||
@config_bp.route('/branches', methods=['POST'])
|
||||
@require_auth('config.edit')
|
||||
def create_branch():
|
||||
@@ -47,10 +85,25 @@ def create_branch():
|
||||
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
|
||||
# If setting as main, clear any existing main
|
||||
if data.get('is_main'):
|
||||
cur.execute("UPDATE branches SET is_main = false WHERE is_main = true")
|
||||
|
||||
cur.execute("""
|
||||
INSERT INTO branches (name, address, phone)
|
||||
VALUES (%s, %s, %s) RETURNING id
|
||||
""", (data['name'], data.get('address'), data.get('phone')))
|
||||
INSERT INTO branches (
|
||||
name, address, phone, is_main,
|
||||
rfc, razon_social, regimen_fiscal, codigo_postal,
|
||||
serie_cfdi, folio_inicial, licencia_fiscal,
|
||||
certificado_pem, llave_pem
|
||||
)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s) RETURNING id
|
||||
""", (
|
||||
data['name'], data.get('address'), data.get('phone'), bool(data.get('is_main')),
|
||||
data.get('rfc'), data.get('razon_social'), data.get('regimen_fiscal'), data.get('codigo_postal'),
|
||||
data.get('serie_cfdi'), data.get('folio_inicial'), data.get('licencia_fiscal'),
|
||||
data.get('certificado_pem'), data.get('llave_pem'),
|
||||
))
|
||||
branch_id = cur.fetchone()[0]
|
||||
conn.commit()
|
||||
cur.close()
|
||||
@@ -58,6 +111,50 @@ def create_branch():
|
||||
return jsonify({'id': branch_id, 'message': 'Branch created'}), 201
|
||||
|
||||
|
||||
@config_bp.route('/branches/<int:branch_id>', methods=['PUT'])
|
||||
@require_auth('config.edit')
|
||||
def update_branch(branch_id):
|
||||
data = request.get_json() or {}
|
||||
conn = get_tenant_conn(g.tenant_id)
|
||||
cur = conn.cursor()
|
||||
|
||||
cur.execute("SELECT id FROM branches WHERE id = %s", (branch_id,))
|
||||
if not cur.fetchone():
|
||||
cur.close(); conn.close()
|
||||
return jsonify({'error': 'Branch not found'}), 404
|
||||
|
||||
# If setting as main, clear any existing main
|
||||
if data.get('is_main'):
|
||||
cur.execute("UPDATE branches SET is_main = false WHERE is_main = true AND id <> %s", (branch_id,))
|
||||
|
||||
updates = []
|
||||
params = []
|
||||
field_map = {
|
||||
'name': 'name', 'address': 'address', 'phone': 'phone',
|
||||
'is_active': 'is_active', 'is_main': 'is_main',
|
||||
'rfc': 'rfc', 'razon_social': 'razon_social',
|
||||
'regimen_fiscal': 'regimen_fiscal', 'codigo_postal': 'codigo_postal',
|
||||
'serie_cfdi': 'serie_cfdi', 'folio_inicial': 'folio_inicial',
|
||||
'licencia_fiscal': 'licencia_fiscal',
|
||||
'certificado_pem': 'certificado_pem', 'llave_pem': 'llave_pem',
|
||||
}
|
||||
for json_key, col in field_map.items():
|
||||
if json_key in data:
|
||||
updates.append(f"{col} = %s")
|
||||
params.append(data[json_key])
|
||||
|
||||
if not updates:
|
||||
cur.close(); conn.close()
|
||||
return jsonify({'error': 'Nothing to update'}), 400
|
||||
|
||||
params.append(branch_id)
|
||||
cur.execute(f"UPDATE branches SET {', '.join(updates)} WHERE id = %s", params)
|
||||
conn.commit()
|
||||
cur.close()
|
||||
conn.close()
|
||||
return jsonify({'ok': True, 'message': 'Branch updated'})
|
||||
|
||||
|
||||
@config_bp.route('/employees', methods=['GET'])
|
||||
@require_auth('config.view')
|
||||
def list_employees():
|
||||
|
||||
Reference in New Issue
Block a user