Compare commits
No commits in common. "fafd5cca1867ddc9f8d0a68c94a23ab5ff0cbca9" and "3bd8c3482b8d58bacfe137f81c8656d84422481b" have entirely different histories.
fafd5cca18
...
3bd8c3482b
13
.vscode/launch.json
vendored
13
.vscode/launch.json
vendored
@ -1,13 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "Run FastAPI",
|
|
||||||
"type": "python",
|
|
||||||
"request": "launch",
|
|
||||||
"module": "uvicorn",
|
|
||||||
"args": ["main:app", "--reload"],
|
|
||||||
"jinja": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@ -1,9 +0,0 @@
|
|||||||
NOME_PROGETTO = "ManganelliOrdiniAPI"
|
|
||||||
VERSIONE = "1.0.0"
|
|
||||||
DEFAULT_CODICE_CLIENTE = "000000"
|
|
||||||
MESSAGGIO_SUCCESSO = "Operazione completata con successo"
|
|
||||||
API_SECRET_KEY = "Z9KPT3QD2MAVNX81HJRY"
|
|
||||||
VALUTA = "EUR"
|
|
||||||
FL_SCOR = "N"
|
|
||||||
TIPO_RIGA_ART="R"
|
|
||||||
FL_OMAG="X"
|
|
||||||
16
database.py
16
database.py
@ -1,16 +0,0 @@
|
|||||||
import pyodbc
|
|
||||||
import os
|
|
||||||
from dotenv import load_dotenv
|
|
||||||
|
|
||||||
load_dotenv()
|
|
||||||
|
|
||||||
conn_str = (
|
|
||||||
f"DRIVER={{ODBC Driver 17 for SQL Server}};"
|
|
||||||
f"SERVER={os.getenv('MSSQL_SERVER')};"
|
|
||||||
f"DATABASE={os.getenv('MSSQL_DATABASE')};"
|
|
||||||
f"UID={os.getenv('MSSQL_USERNAME')};"
|
|
||||||
f"PWD={os.getenv('MSSQL_PASSWORD')}"
|
|
||||||
)
|
|
||||||
|
|
||||||
def get_connection():
|
|
||||||
return pyodbc.connect(conn_str)
|
|
||||||
225
main.py
225
main.py
@ -1,225 +0,0 @@
|
|||||||
import random
|
|
||||||
import string
|
|
||||||
import pyodbc
|
|
||||||
from fastapi import FastAPI, HTTPException, Depends, Query
|
|
||||||
from typing import List
|
|
||||||
from database import get_connection
|
|
||||||
from models import (
|
|
||||||
Cliente, SedeConsegna, Articolo, CodiceRicerca,
|
|
||||||
MetodoPagamento, DisponibilitaArticolo,
|
|
||||||
OrdineTestata, OrdineRiga,Magazzini
|
|
||||||
)
|
|
||||||
from contants import NOME_PROGETTO, MESSAGGIO_SUCCESSO,API_SECRET_KEY,VALUTA,FL_SCOR,TIPO_RIGA_ART,FL_OMAG
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
app = FastAPI()
|
|
||||||
|
|
||||||
def genera_codice_random(lunghezza: int = 50) -> str:
|
|
||||||
caratteri = string.ascii_letters + string.digits # a-z A-Z 0-9
|
|
||||||
return ''.join(random.choices(caratteri, k=lunghezza))
|
|
||||||
|
|
||||||
def genera_cpccchk(lunghezza: int = 10) -> str:
|
|
||||||
lettere = string.ascii_uppercase # solo lettere maiuscole A-Z
|
|
||||||
return ''.join(random.choices(lettere, k=lunghezza))
|
|
||||||
|
|
||||||
def genera_nuovo_codice_cliente():
|
|
||||||
conn = None
|
|
||||||
try:
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("SELECT TOP 1 ANCODICE FROM MANGACONTI WHERE ANCODICE LIKE 'IK%' ORDER BY ANCODICE DESC")
|
|
||||||
row = cursor.fetchone()
|
|
||||||
if row:
|
|
||||||
ultimo_codice = row[0].strip() # es. "IK00000001"
|
|
||||||
numero = int(ultimo_codice[2:]) # parte numerica dopo "IK"
|
|
||||||
nuovo_numero = numero + 1
|
|
||||||
else:
|
|
||||||
nuovo_numero = 1
|
|
||||||
|
|
||||||
nuovo_codice = f"IK{nuovo_numero:08d}" # 8 cifre numeriche con zeri davanti
|
|
||||||
nuovo_codice = nuovo_codice.ljust(15) # aggiunge spazi fino a 15 caratteri totali
|
|
||||||
return nuovo_codice
|
|
||||||
|
|
||||||
except pyodbc.Error as e:
|
|
||||||
# Puoi loggare o rilanciare l'eccezione, o gestirla come vuoi
|
|
||||||
raise Exception(f"Errore DB nel generare codice cliente: {str(e)}")
|
|
||||||
|
|
||||||
finally:
|
|
||||||
if conn:
|
|
||||||
conn.close()
|
|
||||||
|
|
||||||
def verifica_secret_key(secret: str = Query(..., description="Chiave di accesso")):
|
|
||||||
if secret != API_SECRET_KEY:
|
|
||||||
raise HTTPException(status_code=403, detail="Accesso negato: chiave non valida")
|
|
||||||
|
|
||||||
@app.get("/clienti", response_model=List[Cliente])
|
|
||||||
def get_clienti(secret: str = Depends(verifica_secret_key)):
|
|
||||||
print(NOME_PROGETTO)
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("SELECT ANTIPCON, ANCODICE, ANDESCRI, ANPARIVA, ANCODFIS,ANINDIRI FROM MANGACONTI")
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
return [Cliente(antipcon=r[0],ancodice=r[1], andescri=r[2], anpariva=r[3], ancodfis=r[4],anindiri=r[5]) for r in rows]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/sedi-consegna", response_model=List[SedeConsegna])
|
|
||||||
def get_sedi_consegna(codice_cliente: str,secret: str = Depends(verifica_secret_key)):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("SELECT ddtipcon, ddcodice, ddcoddes,ddnomdes,ddindiri,dd___cap,ddlocali,ddprovin,ddcodnaz FROM MANGADES_DIVE WHERE ddtipcon='C' AND ddcodice = ?", codice_cliente)
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
return [SedeConsegna(ddtipcon=r[0], ddcodice=r[1], ddcoddes=r[2],ddnomdes=r[3],ddindiri=r[4],dd___cap=r[5],ddlocali=r[6],ddprovin=r[7],ddcodnaz=r[8]) for r in rows]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/articoli", response_model=List[Articolo])
|
|
||||||
def get_articoli(secret: str = Depends(verifica_secret_key)):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("select ARCODART, ARDESART,ARDESSUP,ARUNMIS1,ARCODIVA from MANGAART_ICOL where ardtobso is null")
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
return [Articolo(arcodart=r[0], ardesart=r[1], ardessup=r[2], arunmis1=r[3]) for r in rows]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/codici-ricerca", response_model=List[CodiceRicerca])
|
|
||||||
def get_codici_ricerca(secret: str = Depends(verifica_secret_key)):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("select CACODICE, CADESART,CADESSUP, CACODART from MANGAKEY_ARTI where CADTOBSO is null")
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
return [CodiceRicerca(cacodice=r[0], cadesart=r[1], cadessup=r[2], cacodart=r[3]) for r in rows]
|
|
||||||
|
|
||||||
|
|
||||||
@app.get("/metodi-pagamento", response_model=List[MetodoPagamento])
|
|
||||||
def get_metodi_pagamento(secret: str = Depends(verifica_secret_key)):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("select PACODICE, PADESCRI from MANGAPAG_AMEN where PADTOBSO is null")
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
return [MetodoPagamento(pacodice=r[0], padescri=r[1]) for r in rows]
|
|
||||||
|
|
||||||
@app.get("/magazzini", response_model=List[Magazzini])
|
|
||||||
def get_magazzini(secret: str = Depends(verifica_secret_key)):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("SELECT * FROM MANGA_IKTOME_MAGAZ")
|
|
||||||
rows = cursor.fetchall()
|
|
||||||
return [Magazzini(mgcodmag=r[0], mgdesmag=r[1]) for r in rows]
|
|
||||||
|
|
||||||
@app.get("/disponibilita", response_model=DisponibilitaArticolo)
|
|
||||||
def get_disponibilita(codice_articolo: str,secret: str = Depends(verifica_secret_key)):
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
cursor.execute("select SLQTAPER from MANGASALDIART WHERE SLCODART = ?", codice_articolo)
|
|
||||||
row = cursor.fetchone()
|
|
||||||
if row:
|
|
||||||
return DisponibilitaArticolo(slcodart=codice_articolo, slqtaper=row[0])
|
|
||||||
raise HTTPException(status_code=404, detail="Articolo non trovato")
|
|
||||||
|
|
||||||
@app.post("/ordini")
|
|
||||||
def crea_ordine(ordine: OrdineTestata):
|
|
||||||
try:
|
|
||||||
conn = get_connection()
|
|
||||||
cursor = conn.cursor()
|
|
||||||
|
|
||||||
# Se manca codice_cliente e ho dati cliente, creo nuovo cliente
|
|
||||||
if not ordine.codice_cliente and ordine.cliente:
|
|
||||||
codice_cliente = genera_nuovo_codice_cliente() # metodo che genera codice tipo "IK00000001 "
|
|
||||||
cursor.execute("""
|
|
||||||
INSERT INTO MANGACONTI (ANTIPCON, ANCODICE, ANDESCRI, ANPARIVA, ANCODFIS, ANINDIRI)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?)
|
|
||||||
""", (
|
|
||||||
"C",
|
|
||||||
codice_cliente,
|
|
||||||
ordine.cliente.andescri,
|
|
||||||
ordine.cliente.anpariva,
|
|
||||||
ordine.cliente.ancodfis,
|
|
||||||
ordine.cliente.anindiri
|
|
||||||
))
|
|
||||||
conn.commit()
|
|
||||||
ordine.codice_cliente = codice_cliente
|
|
||||||
|
|
||||||
if not ordine.codice_cliente:
|
|
||||||
raise HTTPException(status_code=400, detail="Codice cliente mancante e dati cliente non forniti")
|
|
||||||
|
|
||||||
# Genera codice ordine
|
|
||||||
seriale_ordine = genera_codice_random()
|
|
||||||
ordine.orserial = seriale_ordine
|
|
||||||
cpccchk=genera_cpccchk()
|
|
||||||
data_ora_corrente = datetime.now()
|
|
||||||
ordine.or_stato=0 #stato iniziale
|
|
||||||
|
|
||||||
cursor.execute("""
|
|
||||||
INSERT INTO MANGAZORDWEBM (orserial, or_stato, ortipdoc, ornumdoc, oralfdoc, ortipcon, orcodcon, orcodage, orcodpag,
|
|
||||||
ortotord, ordatdoc,cpccchk,orcodval,orflscor,oralfest,utdc,utdv,orcodpor,or__note,orrifest,orvalnaz,orcaoval,orvalacc)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
""", (
|
|
||||||
ordine.orserial,
|
|
||||||
ordine.or_stato,
|
|
||||||
ordine.ortipdoc,
|
|
||||||
ordine.ornumdoc,
|
|
||||||
ordine.oralfdoc,
|
|
||||||
ordine.ortipcon,
|
|
||||||
ordine.codice_cliente,
|
|
||||||
ordine.orcodage,
|
|
||||||
ordine.orcodpag,
|
|
||||||
ordine.ortotord,
|
|
||||||
ordine.ordatdoc,
|
|
||||||
cpccchk,
|
|
||||||
VALUTA,
|
|
||||||
FL_SCOR,
|
|
||||||
" ",
|
|
||||||
data_ora_corrente,
|
|
||||||
data_ora_corrente,
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
1,
|
|
||||||
" "
|
|
||||||
))
|
|
||||||
#conn.commit()
|
|
||||||
cursor.execute("SELECT SCOPE_IDENTITY()")
|
|
||||||
id_ordine = cursor.fetchone()[0]
|
|
||||||
|
|
||||||
# Inserisce righe ordine
|
|
||||||
for riga in ordine.righe:
|
|
||||||
cproword=riga.cprownum * 10
|
|
||||||
cursor.execute("""
|
|
||||||
INSERT INTO MANGAZORDWEBD (orserial, cprownum, ordesart, ordessup, orqtamov, orcodart,orcodice,
|
|
||||||
cproword,ortiprig,orcodvar,orunimis,orcodiva,orflomag,orcodlis,orcontra,
|
|
||||||
orscolis,orprolis,orprosco,cpccchk)
|
|
||||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
||||||
""", (
|
|
||||||
ordine.orserial,
|
|
||||||
riga.cprownum,
|
|
||||||
riga.ordesart,
|
|
||||||
riga.ordessup,
|
|
||||||
riga.orqtamov,
|
|
||||||
riga.orcodart,
|
|
||||||
riga.orcodice,
|
|
||||||
cproword,
|
|
||||||
TIPO_RIGA_ART,
|
|
||||||
" ",
|
|
||||||
riga.orunimis,
|
|
||||||
riga.orcodiva,
|
|
||||||
FL_OMAG,
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
" ",
|
|
||||||
genera_cpccchk()
|
|
||||||
))
|
|
||||||
conn.commit()
|
|
||||||
|
|
||||||
return {"orserial": ordine.orserial, "messaggio": "Ordine creato con successo"}
|
|
||||||
|
|
||||||
except pyodbc.Error as e:
|
|
||||||
conn.rollback()
|
|
||||||
raise HTTPException(status_code=500, detail=f"Errore DB: {str(e)}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
raise HTTPException(status_code=500, detail=f"Errore generico: {str(e)}")
|
|
||||||
|
|
||||||
finally:
|
|
||||||
conn.close()
|
|
||||||
76
models.py
76
models.py
@ -1,76 +0,0 @@
|
|||||||
from decimal import Decimal
|
|
||||||
from datetime import datetime
|
|
||||||
from pydantic import BaseModel,Field
|
|
||||||
from typing import List, Optional
|
|
||||||
|
|
||||||
class Cliente(BaseModel):
|
|
||||||
antipcon: str= Field(..., description="Chiave primaria")
|
|
||||||
ancodice: str= Field(..., description="Chiave primaria")
|
|
||||||
andescri: Optional[str] = Field(None, description="Ragione sociale")
|
|
||||||
anpariva: Optional[str] = Field(None, description="Partita iva")
|
|
||||||
ancodfis: Optional[str] = Field(None, description="codice Fiscale")
|
|
||||||
anindiri: Optional[str] = Field(None, description="Indirizzo")
|
|
||||||
|
|
||||||
class SedeConsegna(BaseModel):
|
|
||||||
ddtipcon: str= Field(description="Chiave primaria")
|
|
||||||
ddcodice: str= Field(description="Chiave primaria")
|
|
||||||
ddcoddes: str= Field(description="Chiave primaria")
|
|
||||||
ddnomdes: Optional[str] = Field(None, description="Nome della sede di destinazione")
|
|
||||||
ddindiri: Optional[str] = Field(None, description="Indirizzo della sede di destinazione")
|
|
||||||
dd___cap: Optional[str] = Field(None, description="CAP della sede di destinazione")
|
|
||||||
ddlocali: Optional[str] = Field(None, description="località della sede di destinazione")
|
|
||||||
ddprovin: Optional[str] = Field(None, description="Provincia della sede di destinazione")
|
|
||||||
ddcodnaz: Optional[str] = Field(None, description="nazione della sede di destinazione")
|
|
||||||
|
|
||||||
class Articolo(BaseModel):
|
|
||||||
arcodart: str= Field(..., description="Chiave primaria")
|
|
||||||
ardesart: Optional[str]= Field(None, description="Descrizione articolo")
|
|
||||||
ardessup: Optional[str]= Field(None, description="Descrizione supplementare")
|
|
||||||
arunmis1: Optional[str]= Field(None, description="Unità di misura")
|
|
||||||
arcodiva: Optional[str]= Field(None, description="Codice Iva articolo")
|
|
||||||
|
|
||||||
class CodiceRicerca(BaseModel):
|
|
||||||
cacodice: str = Field(..., description="Chiave primaria")
|
|
||||||
cadesart: Optional[str]= Field(None, description="Descrizione codice ricerca")
|
|
||||||
cacodart: Optional[str]= Field(None, description="articolo principale")
|
|
||||||
cadessup: Optional[str]= Field(None, description="Descrizione supplementare codice ricerca")
|
|
||||||
|
|
||||||
class MetodoPagamento(BaseModel):
|
|
||||||
pacodice: str = Field(..., description="Chiave primaria")
|
|
||||||
padescri: Optional[str]= Field(None, description="Descrizione metodo pagamento")
|
|
||||||
|
|
||||||
class Magazzini(BaseModel):
|
|
||||||
mgcodmag: str = Field(..., description="Chiave primaria")
|
|
||||||
mgdesmag: Optional[str]= Field(None, description="Descrizione magazzino")
|
|
||||||
|
|
||||||
class DisponibilitaArticolo(BaseModel):
|
|
||||||
slqtaper: int = Field(..., description="qta disponibile")
|
|
||||||
slcodart: str = Field(..., description="Chiave primaria")
|
|
||||||
|
|
||||||
class OrdineRiga(BaseModel):
|
|
||||||
orserial: str = Field(..., description="Chiave primaria")
|
|
||||||
cprownum: int = Field(..., description="num riga Chiave primaria")
|
|
||||||
orcodart: str = Field(..., description="codice articolo")
|
|
||||||
ordesart: str = Field(..., description="descrizione articolo")
|
|
||||||
ordessup: str = Field(..., description="descrizione suppl. articolo")
|
|
||||||
orqtamov: int = Field(..., description="quantità")
|
|
||||||
orcodice: str = Field(..., description="chiave di ricerca articolo")
|
|
||||||
orunimis: str = Field(..., description="unità di misura")
|
|
||||||
orcodiva: str = Field(..., description="codice iva articolo")
|
|
||||||
|
|
||||||
|
|
||||||
class OrdineTestata(BaseModel):
|
|
||||||
orserial: str = Field(..., description="Chiave primaria")
|
|
||||||
or_stato: int = Field(..., description="Stato ordine def: 1")
|
|
||||||
ortipdoc: str = Field(..., description="Tipo documento def: OR=ordine")
|
|
||||||
ornumdoc: Optional[Decimal]= Field(None, description="Numero documento")
|
|
||||||
oralfdoc: Optional[str]= Field(None, description="Sezionale documento ordine")
|
|
||||||
ortipcon: str = Field(..., description="Tipo anagrafica Clienti=C")
|
|
||||||
orcodcon: str = Field(..., description="codice anagrafica cliente")
|
|
||||||
orcodage: Optional[str]= Field(None, description="codice agente")
|
|
||||||
orcodpag: Optional[str]= Field(None, description="codice pagamento")
|
|
||||||
ortotord: Optional[Decimal]= Field(None, description="Totale ordine")
|
|
||||||
ordatdoc: datetime = Field(..., description="data documento")
|
|
||||||
righe: List[OrdineRiga]
|
|
||||||
cliente: Optional[Cliente] = None
|
|
||||||
codice_cliente: Optional[str]= Field(None, description="Codice cliente nel caso l'anagrafica esista")
|
|
||||||
BIN
requirements.txt
BIN
requirements.txt
Binary file not shown.
Loading…
Reference in New Issue
Block a user