|
|
@@ -4,18 +4,44 @@
|
|
|
|
|
|
@author: olivier.massot, févr. 2018
|
|
|
'''
|
|
|
+from datetime import datetime
|
|
|
import logging
|
|
|
import sys
|
|
|
|
|
|
from path import Path
|
|
|
|
|
|
from core import logconf
|
|
|
+from core.model import Model
|
|
|
from core.pde import FacturesDb, AnalytiqueDb, CommunDb
|
|
|
|
|
|
|
|
|
logger = logging.getLogger("analytique2facture")
|
|
|
logconf.start("analytique2facture", logging.DEBUG)
|
|
|
|
|
|
+class EnTete(Model):
|
|
|
+ _FIELDS = ["lngPieceId", "lngDocId", "lngTiersId", "lngASTRE", "strCodeProduit", "bytTitreId",
|
|
|
+ "strCodeAdresse", "strAdresse1", "strAdresse2", "strAdresse3", "strAdresse4", "strAdresse5",
|
|
|
+ "strPaysIdIso3166", "bytTVATiersId", "strTelephone", "strTelecopie", "strPortable",
|
|
|
+ "strEMail", "strWeb", "strLangueIdIso639", "strCompteComptable", "strDeviseIdIso4217",
|
|
|
+ "bytReglementId", "strReglement", "bytClasseTarifId", "bytClasseRemiseTiersId", "bytNbExFacture",
|
|
|
+ "strStatTiers", "bytSituationIdPrincipale", "bytSituationIdSecondaire", "memObsEntete",
|
|
|
+ "memObsPied", "memObsInterne", "bytTypeDocumentId", "strStatDocEntete", "dblCoursDevise",
|
|
|
+ "dtmCoursDevise", "dtmCreation", "strUserIdCreation", "dtmLastMod", "strUserIdLastMod",
|
|
|
+ "dtmPiece", "dtmFirstPrint", "dtmLastPrint", "dtmCompta", "dtmLivraison", "dtmEcheance",
|
|
|
+ "bytTypeTarif", "idEtatEmission"]
|
|
|
+
|
|
|
+class Ligne(Model):
|
|
|
+ _FIELDS = ["lngPieceId", "intLigneId", "strArticleId", "strArticle", "bytTVAArticleId", "bytTVAId", "dblTVATaux",
|
|
|
+ "dblQte", "bytUniteIdQuantite", "bytNbDecQuantite", "bytUniteIdPrix", "dblCnvUniteCoef",
|
|
|
+ "bytNbDecQuantiteConvertie", "dblPUhtBrutDev", "dblPUttcBrutDev", "dblPUhtNetDev", "dblPUttcNetDev",
|
|
|
+ "bytNbDecPU", "dblPThtBrutDev", "dblPTttcBrutDev", "dblPThtNetDev", "dblPTttcNetDev", "strStatArticle",
|
|
|
+ "strStatDocLigne", "dblTauxRemise1", "dblTauxRemise2", "dblTauxRemise3", "blnPrestation",
|
|
|
+ "strCompteComptable", "memObs", "memObsInterne", "bytLigneSousTotal", "intLigneIdRattachement",
|
|
|
+ "blnLigneVisible", "blnLigneGeneree", "bytGenerateurId", "dblPUhtBrut", "dblPUttcBrut", "dblPUhtNet",
|
|
|
+ "dblPUttcNet", "dblPThtBrut", "dblPTttcBrut", "dblPThtNet", "dblPTttcNet", "bytClasseRemiseArticleId",
|
|
|
+ "dblPUSaisie", "strPUAff", "strQteAff"]
|
|
|
+
|
|
|
+
|
|
|
# # POUR TESTER, décommenter les lignes suivantes
|
|
|
##-----------------------------------------------
|
|
|
|
|
|
@@ -35,26 +61,223 @@ facture_db = FacturesDb(autocommit=False)
|
|
|
# Connexion à CommunDb
|
|
|
commun_db = CommunDb(autocommit=False)
|
|
|
|
|
|
-mois_facturation = input("Veuillez renseigner le mois de facturation ('q' pour quitter): ") # Format: voir avec jacky
|
|
|
-if mois_facturation == 'q':
|
|
|
- sys.exit(1)
|
|
|
+# mois_facturation = input("Veuillez renseigner le mois de facturation ('q' pour quitter): ") # Format: voir avec jacky
|
|
|
+# if mois_facturation == 'q':
|
|
|
+# sys.exit(1)
|
|
|
+mois_facturation = "1801"
|
|
|
|
|
|
sql = """SELECT * FROM tbl_Tarification
|
|
|
WHERE strStatut='A facturer'
|
|
|
ORDER BY DblAffaireId, DblTarifId DESC;
|
|
|
"""
|
|
|
|
|
|
-if not analytique_db.exists(sql):
|
|
|
+# Liste les interventions à facturer par affaire
|
|
|
+a_facturer = {}
|
|
|
+for interv in analytique_db.read(sql):
|
|
|
+ if not interv.DblAffaireId in a_facturer.keys():
|
|
|
+ a_facturer[interv.DblAffaireId] = []
|
|
|
+ a_facturer[interv.DblAffaireId].append(interv)
|
|
|
+
|
|
|
+if not a_facturer:
|
|
|
logger.info("Aucune facture à créer - Opération annulée")
|
|
|
sys.exit(1)
|
|
|
|
|
|
|
|
|
-# On va créer une facture commune à toutes les interventions 'à facturer' d'une même affaire (cad qui ont un numéro de pièce commun)
|
|
|
-# > Pour chaque facture, on va ajouter une ligne dans tblPieceEntete, et une ligne par intervention dans tblPieceLigne
|
|
|
+# Pour chaque facture, on va ajouter une ligne dans tblPieceEntete, et une ligne par intervention dans tblPieceLigne
|
|
|
# > NB: On ne touche pas aux interventions de cette affaire qui ont déja été facturées
|
|
|
|
|
|
-for tarif in analytique_db.read(sql):
|
|
|
- pass
|
|
|
+
|
|
|
+for affaireId, interventions in a_facturer.items():
|
|
|
+
|
|
|
+ piece_id = facture_db.first("SELECT Max(lngpieceId) As Id FROM tblPieceEntete").Id + 1
|
|
|
+ if str(piece_id)[:2] != mois_facturation[:2]:
|
|
|
+ # On démarre une nouvelle année
|
|
|
+ piece_id = 10000 * int(mois_facturation[:2]) + 1
|
|
|
+
|
|
|
+ affaire = analytique_db.first("SELECT * FROM tbl_Affaires WHERE dblAffaireId={}".format(affaireId))
|
|
|
+ if not affaire:
|
|
|
+ logger.error("Affaire {} - L'affaire n'existe pas dans tbl_Affaires".format(affaireId))
|
|
|
+ continue
|
|
|
+
|
|
|
+ if not affaire.strMOId:
|
|
|
+ logger.error("Affaire {} - Destinataire manquant dans tbl_Affaires".format(affaireId))
|
|
|
+ continue
|
|
|
+
|
|
|
+ destinataire = commun_db.first("SELECT * FROM tblTiers WHERE lngTiersId={}".format(affaire.strMOId))
|
|
|
+ if not destinataire:
|
|
|
+ logger.warning("Affaire {} - Destinataire introuvable dans tblTiers ('{}'), remplacé par tiers n°190".format(affaireId, affaire.strMOId))
|
|
|
+ destinataire = commun_db.first("SELECT * FROM tblTiers WHERE lngTiersId=190")
|
|
|
+
|
|
|
+ entete = EnTete()
|
|
|
+ entete.lngPieceId = piece_id
|
|
|
+
|
|
|
+ # on recupere les champs de tblTiers depuis le champ lngASTRE (champ n°3) jusqu'au champ strCodeProduit (champ n°26)
|
|
|
+ # ATTENTION: le champ 'strPaysIdIso3166-A2' n'etant pas un nom de propriété valide, il est renomme en strPaysIdIso3166 dans le modèle.
|
|
|
+ entete.lngTiersId = destinataire.lngTiersId
|
|
|
+ entete.lngASTRE = destinataire.lngASTRE
|
|
|
+ entete.bytTitreId = destinataire.bytTitreId
|
|
|
+ entete.strCodeAdresse = destinataire.strCodeAdresse
|
|
|
+ entete.strAdresse1 = destinataire.strAdresse1
|
|
|
+ entete.strAdresse2 = destinataire.strAdresse2
|
|
|
+ entete.strAdresse3 = destinataire.strAdresse3
|
|
|
+ entete.strAdresse4 = destinataire.strAdresse4
|
|
|
+ entete.strAdresse5 = destinataire.strAdresse5
|
|
|
+ entete.strPaysIdIso3166 = destinataire.field_11
|
|
|
+ entete.bytTVATiersId = destinataire.bytTVATiersId
|
|
|
+ entete.strTelephone = destinataire.strTelephone
|
|
|
+ entete.strTelecopie = destinataire.strTelecopie
|
|
|
+ entete.strPortable = destinataire.strPortable
|
|
|
+ entete.strEMail = destinataire.strEMail
|
|
|
+ entete.strWeb = destinataire.strWeb
|
|
|
+ entete.strLangueIdIso639 = destinataire.strLangueIdIso639
|
|
|
+ entete.strCompteComptable = destinataire.strCompteComptable
|
|
|
+ entete.strDeviseIdIso4217 = destinataire.strDeviseIdIso4217
|
|
|
+ entete.bytReglementId = destinataire.bytReglementId
|
|
|
+ entete.bytClasseTarifId = destinataire.bytClasseTarifId
|
|
|
+ entete.bytNbExFacture = destinataire.bytNbExFacture
|
|
|
+ entete.bytClasseRemiseTiersId = destinataire.bytClasseRemiseTiersId
|
|
|
+ entete.bytNbExFacture = destinataire.bytNbExFacture
|
|
|
+ entete.strStatTiers = destinataire.strStatTiers
|
|
|
+
|
|
|
+ # Valeurs fixes
|
|
|
+ entete.lngDocId = 0
|
|
|
+ entete.strLangueIdIso639 = "fr"
|
|
|
+ entete.strDeviseIdIso4217 = "EUR"
|
|
|
+ entete.bytReglementId = 1
|
|
|
+ entete.bytClasseTarifId = 1
|
|
|
+ entete.bytClasseRemiseTiersId = 1
|
|
|
+ entete.bytNbExFacture = 1
|
|
|
+ entete.strStatTiers = ""
|
|
|
+ entete.bytSituationIdPrincipale = 2
|
|
|
+ entete.bytSituationIdSecondaire = 41
|
|
|
+ entete.bytTypeDocumentId = 50
|
|
|
+ entete.strStatDocEntete = "PI00"
|
|
|
+ entete.dblCoursDevise = 1
|
|
|
+ entete.dtmCoursDevise = datetime(2002, 1, 1)
|
|
|
+ entete.bytTypeTarif = 1
|
|
|
+ entete.strReglement = "POUR ACQUITTER CET ETAT DE REDEVANCE,\nATTENDEZ IMPERATIVEMENT DE RECEVOIR LE TITRE DE PERCEPTION EMANANT DE LA PAIERIE DEPARTEMENTALE"
|
|
|
+
|
|
|
+ # Commentaires
|
|
|
+ entete.memObsEntete = """[Mois : {mois_facturation}]\n [Lieu de travail : ] {affaire.strLieux}\n [V/Cde : ] {affaire.Ref} du {affaire.dtmCommande}
|
|
|
+ """.format(mois_facturation, affaire)
|
|
|
+
|
|
|
+ entete.memObsInterne = "N° Affaire analytique : {}".format(affaire.stLiaisonControle)
|
|
|
+
|
|
|
+ entete.strStatDocEntete = "PC00" if ("/" in affaire.stLiaisonControle or affaire.stLiaisonControle == "RDBOEC") else "PT00"
|
|
|
+
|
|
|
+ entete.strUserIdCreation = "analytique2facture"
|
|
|
+ entete.strUserIdLastMod = "analytique2facture"
|
|
|
+
|
|
|
+ i = 0
|
|
|
+ lignes = []
|
|
|
+ for interv in interventions:
|
|
|
+ i += 1
|
|
|
+
|
|
|
+ ligne = Ligne()
|
|
|
+ ligne.lngPieceId = piece_id
|
|
|
+ ligne.intLigneId = i
|
|
|
+ ligne.strArticleId = interv.strArticleId
|
|
|
+
|
|
|
+ article = commun_db.first("SELECT * FROM tblArticle WHERE strArticleId='{}'".format(interv.strArticleId))
|
|
|
+ ligne.strArticle = article.strArticle if article else ""
|
|
|
+ ligne.bytTVAArticleId = article.bytTVAArticleId if article else 0
|
|
|
+
|
|
|
+ tva = commun_db.first("SELECT bytTVAId FROM tblTVA WHERE bytTVATiersId={} AND bytTVAArticleId={}".format(destinataire.bytTVATiersId, ligne.bytTVAArticleId))
|
|
|
+ ligne.bytTVAId = tva.bytTVAId
|
|
|
+
|
|
|
+ taux = commun_db.first("SELECT dblTVATaux FROM tblTVATaux WHERE bytTVAId={}".format(ligne.bytTVAId))
|
|
|
+ ligne.dblTVATaux = taux.dblTVATaux
|
|
|
+
|
|
|
+ ligne.dblQte = interv.dblquantite
|
|
|
+
|
|
|
+ unite = commun_db.first("SELECT * FROM tblUnite WHERE strUniteCourt='{}'".format(interv.strunite))
|
|
|
+ ligne.bytUniteIdQuantite = unite.bytUniteId
|
|
|
+ ligne.bytUniteIdPrix = unite.bytUniteId
|
|
|
+ ligne.dblPUhtBrutDev = interv.dblPrixUnitaire
|
|
|
+ ligne.dblPUhtNetDev = interv.dblPrixUnitaire
|
|
|
+ ligne.dblPThtNetDev = interv.dblPrixTotal
|
|
|
+ ligne.blnPrestation = True
|
|
|
+ ligne.memObs = "Intervention le {}{}".format(interv.dtmFin,
|
|
|
+ "\nRapport no : {}".format(interv.strRapportId) if interv.strRapportId else "")
|
|
|
+ ligne.blnLigneGeneree = True
|
|
|
+ ligne.bytGenerateurId = 1
|
|
|
+ ligne.dblPThtNet = interv.dblPrixTotal
|
|
|
+ ligne.bytClasseRemiseArticleId = 1
|
|
|
+ ligne.dblPUSaisie = interv.dblPrixUnitaire
|
|
|
+ ligne.strPUAff = "{} EUR/{}".format(interv.dblPrixUnitaire, interv.strunite)
|
|
|
+ ligne.strQteAff = "{:03.02f} {}".format(interv.dblquantite, interv.strunite)
|
|
|
+
|
|
|
+ lignes.append(ligne)
|
|
|
+
|
|
|
+ # Toutes les lignes ont été ajoutées: on créé maintenant les lignes de totaux
|
|
|
+ montant_ht = sum([ligne.dblPThtNetDev for ligne in lignes])
|
|
|
+ taux_tva = lignes[0].dblTVATaux
|
|
|
+ montant_tva = (0.01 * taux_tva) * montant_ht
|
|
|
+ montant_ttc = montant_ht + montant_tva
|
|
|
+
|
|
|
+ # Sous-total HT (ligne 32000)
|
|
|
+ ligne_total_1 = Ligne()
|
|
|
+ ligne_total_1.lngPieceId = piece_id
|
|
|
+ ligne_total_1.intLigneId = 32000
|
|
|
+ ligne_total_1.strArticleId = ""
|
|
|
+ ligne_total_1.strArticle = "Total H.T."
|
|
|
+ ligne_total_1.dblPThtNetDev = montant_ht
|
|
|
+ ligne_total_1.dblPTttcNetDev = montant_ttc
|
|
|
+ ligne_total_1.bytLigneSousTotal = 10
|
|
|
+ ligne_total_1.blnLigneVisible = False
|
|
|
+ ligne_total_1.dblPThtNet = montant_ht
|
|
|
+ ligne_total_1.dblPTttcNet = round(montant_ttc, 2)
|
|
|
+ ligne_total_1.dblPUSaisie = round(montant_ht, 2)
|
|
|
+ ligne_total_1.strPUAff = "{:03.02f} EUR".format(montant_ht)
|
|
|
+
|
|
|
+ lignes.append(ligne_total_1)
|
|
|
+
|
|
|
+ # Sous-total TVA (ligne 32001)
|
|
|
+ ligne_total_2 = Ligne()
|
|
|
+ ligne_total_2.lngPieceId = piece_id
|
|
|
+ ligne_total_2.intLigneId = 32001
|
|
|
+ ligne_total_2.strArticleId = ""
|
|
|
+ ligne_total_2.strArticle = """TVA 1 : {taux_tva}% H.T. : {montant_ht:03.02f} T.V.A. :
|
|
|
+ {montant_tva:03.02f} T.T.C. : {montant_ttc:03.02f}:""".format(taux_tva=taux_tva,
|
|
|
+ montant_ht=montant_ht,
|
|
|
+ montant_tva=montant_tva,
|
|
|
+ montant_ttc=montant_ttc)
|
|
|
+ ligne_total_2.bytTVAId = lignes[0].bytTVAId
|
|
|
+ ligne_total_2.dblTVATaux = taux_tva
|
|
|
+ ligne_total_2.dblPThtNetDev = montant_ht
|
|
|
+ ligne_total_2.dblPTttcNetDev = montant_ttc
|
|
|
+ ligne_total_2.bytLigneSousTotal = 11
|
|
|
+ ligne_total_2.blnLigneVisible = False
|
|
|
+ ligne_total_2.dblPThtNet = montant_ht
|
|
|
+ ligne_total_2.dblPTttcNet = round(ligne_total_1.dblPTttcNetDev, 2)
|
|
|
+ ligne_total_2.dblPUSaisie = round(montant_tva, 2)
|
|
|
+ ligne_total_2.strPUAff = "{:03.02f} EUR".format(montant_tva)
|
|
|
+
|
|
|
+ lignes.append(ligne_total_2)
|
|
|
+
|
|
|
+ # Sous-total TTC (ligne 32500)
|
|
|
+ ligne_total_3 = Ligne()
|
|
|
+ ligne_total_3.lngPieceId = piece_id
|
|
|
+ ligne_total_3.intLigneId = 32500
|
|
|
+ ligne_total_3.strArticleId = ""
|
|
|
+ ligne_total_3.strArticle = "Total T.T.C."
|
|
|
+ ligne_total_3.dblPThtNetDev = montant_ht
|
|
|
+ ligne_total_3.dblPTttcNetDev = montant_ttc
|
|
|
+ ligne_total_3.bytLigneSousTotal = 12
|
|
|
+ ligne_total_3.blnLigneVisible = False
|
|
|
+ ligne_total_3.dblPThtNet = montant_ttc
|
|
|
+ ligne_total_3.dblPTttcNet = round(montant_ttc, 2)
|
|
|
+ ligne_total_3.dblPUSaisie = round(montant_ttc, 2)
|
|
|
+ ligne_total_3.strPUAff = "{:03.02f} EUR".format(montant_ttc)
|
|
|
+
|
|
|
+ lignes.append(ligne_total_3)
|
|
|
+
|
|
|
+
|
|
|
+# Insérer les données dans tbl_Facture
|
|
|
+
|
|
|
+# Maj tbl_Tarification.strNumFacture et strStatut pour marquer la ligne comme facturée.
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
|