mirror of
https://github.com/Noettore/fattureCCSR.git
synced 2025-10-14 19:26:39 +02:00
Merge pull request #3 from Noettore/pythonRewrite
Complete python rewrite
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -24,4 +24,5 @@ __pycache__/*
|
||||
*.csv
|
||||
*.xml
|
||||
*.ipynb
|
||||
*.spec
|
||||
*.spec
|
||||
*.wxg
|
||||
|
12
README.md
12
README.md
@@ -1,17 +1,13 @@
|
||||
# fattureSanRossore
|
||||
# fattureCCSR
|
||||
|
||||
[](LICENSE.md) [](#) [](https://github.com/Noettore/fattureSanRossore/commit/master)
|
||||
|
||||
## The Project
|
||||
|
||||
Here you can find two distinct utilities:
|
||||
- [`fattureDownloader`](fattureDownloader), a Go script which lets you download invoices in pdf starting from an .xls file;
|
||||
- [`traf2000Converter`](traf2000Converter), a Python script which converts a .csv file to a TRAF2000 text file for being imported in TeamSystem's accounting software.
|
||||
This utility has the only purpose of facilitate the tasks of downloading invoices and generating TeamSystem's TRAF2000 record from a CCSR report.
|
||||
It allows the user to authenticate to the CCSR SQL Server Reporting Services (SSRS) and after specifying a time interval it download the suitable report and lets generate the TRAF2000 record or download and merge all the invoices issued in that period.
|
||||
|
||||
## Author
|
||||
|
||||
- [**Ettore Dreucci**](https://ettore.dreucci.it)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](/LICENSE.md) file for details
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](/LICENSE.md) file for details
|
||||
|
@@ -1,48 +0,0 @@
|
||||
# fattureSanRossore Downloader
|
||||
|
||||
[](../LICENSE.md) [](#) [](#) [](https://github.com/Noettore/fattureSanRossore/commit/master)
|
||||
|
||||
## Getting Started
|
||||
|
||||
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Go](https://golang.org/) v1.14 or greater
|
||||
- [LibreOffice](https://www.libreoffice.org)
|
||||
|
||||
### Installing
|
||||
|
||||
To download and install follow this steps:
|
||||
|
||||
1. Clone this repo (or download it):
|
||||
|
||||
`$ git clone https://github.com/Noettore/fattureSanRossore`
|
||||
|
||||
2. Download dependencies, build and install:
|
||||
|
||||
```
|
||||
$ cd path/to/fattureSanRossore/fattureDownloader
|
||||
$ go install
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
To run you can simply execute the built binary.
|
||||
|
||||
Follow the instruction in the [manual](manual/Manuale.md) (only in italian)
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [github.com/extrame/xls](https://github.com/extrame/xls)
|
||||
- [github.com/pdfcpu/pdfcpu](https://github.com/pdfcpu/pdfcpu/pkg/api)
|
||||
- [github.com/sqweek/dialog](https://github.com/sqweek/dialog)
|
||||
- [mvdan.cc/xurls](https://mvdan.cc/xurls/v2)
|
||||
|
||||
## Author
|
||||
|
||||
- [**Ettore Dreucci**](https://ettore.dreucci.it)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](../LICENSE.md) file for details
|
@@ -1,313 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/extrame/xls"
|
||||
"github.com/pdfcpu/pdfcpu/pkg/api"
|
||||
"github.com/sqweek/dialog"
|
||||
"mvdan.cc/xurls/v2"
|
||||
)
|
||||
|
||||
var tmpDir string
|
||||
var outDir string
|
||||
var mw io.Writer
|
||||
var logPath string
|
||||
var exitWithError bool
|
||||
|
||||
func getInvoiceIDs(fileName string) []string {
|
||||
xlFile, err := xls.Open(fileName, "utf-8")
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile aprire il file xls: %v\n", err)
|
||||
}
|
||||
|
||||
sheet := xlFile.GetSheet(0)
|
||||
if sheet == nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile aprire il foglio nell'xls: %v\n", err)
|
||||
}
|
||||
|
||||
var invoiceIDs []string
|
||||
|
||||
for i := 0; i <= int(sheet.MaxRow); i++ {
|
||||
row := sheet.Row(i)
|
||||
if strings.Contains(row.Col(8), "CCSR") {
|
||||
id := strings.ReplaceAll(row.Col(8), "/", "-")
|
||||
invoiceIDs = append(invoiceIDs, id)
|
||||
}
|
||||
}
|
||||
return invoiceIDs
|
||||
}
|
||||
|
||||
func convertXLStoFODS(fileName string) string {
|
||||
var sofficePath = "libreoffice"
|
||||
if runtime.GOOS == "windows" {
|
||||
sofficePath = filepath.FromSlash("C:/Program Files/LibreOffice/program/soffice.exe")
|
||||
}
|
||||
cmd := exec.Command(sofficePath, "--convert-to", "fods", "--outdir", outDir, fileName)
|
||||
cmd.Stdout = mw
|
||||
cmd.Stderr = mw
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile convertire l'XLS in FODS: %v\n", err)
|
||||
}
|
||||
return (outDir + "/" + strings.TrimSuffix(filepath.Base(fileName), filepath.Ext(fileName)) + ".fods")
|
||||
}
|
||||
|
||||
func getInvoiceURLs(fileName string) []string {
|
||||
fods := convertXLStoFODS(fileName)
|
||||
f, err := os.Open(fods)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile aprire il FODS convertito: %v\n", err)
|
||||
}
|
||||
defer func() {
|
||||
err = f.Close()
|
||||
if err != nil {
|
||||
log.Printf("Impossibile chiudere il file %v: %v\n", fods, err)
|
||||
}
|
||||
}()
|
||||
var invoiceURLs []string
|
||||
s := bufio.NewScanner(f)
|
||||
for s.Scan() {
|
||||
line := s.Text()
|
||||
if strings.Contains(line, "http://report.casadicurasanrossore.it:9146/files/get?type=invoice&id=") {
|
||||
url := xurls.Strict().FindString(line)
|
||||
url = strings.ReplaceAll(url, "&", "&")
|
||||
invoiceURLs = append(invoiceURLs, url)
|
||||
}
|
||||
}
|
||||
if err := s.Err(); err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile leggere dal file %v: %v\n", fods, err)
|
||||
}
|
||||
return invoiceURLs
|
||||
}
|
||||
|
||||
func checkFile(fileName string) string {
|
||||
_, err := os.Stat(fileName)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Errore nell'apertura del file %v: %v\n", fileName, err)
|
||||
}
|
||||
absPath, err := filepath.Abs(fileName)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile recuperare il percorso assoluto del file %v: %v\n", fileName, err)
|
||||
}
|
||||
return absPath
|
||||
}
|
||||
|
||||
func downloadFile(fileName string, url string) error {
|
||||
resp, err := http.Get(url)
|
||||
|
||||
if err != nil {
|
||||
resp.Body.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
out, err := os.Create(fileName)
|
||||
if err != nil {
|
||||
out.Close()
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = io.Copy(out, resp.Body)
|
||||
out.Close()
|
||||
resp.Body.Close()
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func createTmpDir() string {
|
||||
dir := filepath.FromSlash(tmpDir + "/fattureSanRossore")
|
||||
|
||||
if _, err := os.Stat(dir); err == nil {
|
||||
log.Printf("Pulizia della directory temporanea pre-esistente")
|
||||
err := os.RemoveAll(dir)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile eliminare la directory temporanea pre-esistente %v: %v\n", dir, err)
|
||||
}
|
||||
} else if !os.IsNotExist(err) {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile eseguire lo stat sulla directory temporanea %v: %v\n", dir, err)
|
||||
}
|
||||
err := os.Mkdir(dir, os.ModePerm)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile creare la directory temporanea di salvataggio %v: %v\n", dir, err)
|
||||
}
|
||||
|
||||
return dir
|
||||
}
|
||||
|
||||
func downloadInvoices(ids []string, urls []string) []string {
|
||||
if len(ids) != len(urls) {
|
||||
exitWithError = true
|
||||
log.Printf("Il numero di fatture da scaricare non corrisponde al numero di URL individuati nel file. IDs: %d/URLs: %d", len(ids), len(urls))
|
||||
}
|
||||
|
||||
wg := sync.WaitGroup{}
|
||||
sem := make(chan bool, 30)
|
||||
mu := sync.Mutex{}
|
||||
invoiceNum := minOf(len(ids), len(urls))
|
||||
downloadCount := 0
|
||||
downloadedFiles := make([]string, 0)
|
||||
|
||||
log.Printf("Inizio il download di %d fatture\n", invoiceNum)
|
||||
for i := 0; i < invoiceNum; i++ {
|
||||
id := ids[i]
|
||||
url := urls[i]
|
||||
out := filepath.FromSlash(outDir + "/" + id + ".pdf")
|
||||
|
||||
wg.Add(1)
|
||||
go func(i int) {
|
||||
sem <- true
|
||||
log.Printf("Scaricamento di %v\n", id)
|
||||
err := downloadFile(out, url)
|
||||
if err != nil {
|
||||
log.Printf("Impossibile scaricare il file %v: %v\n", url, err)
|
||||
} else if api.ValidateFile(out, nil) != nil {
|
||||
log.Printf("Errore nella validazione del file %v\n", out)
|
||||
} else {
|
||||
mu.Lock()
|
||||
downloadedFiles = append(downloadedFiles, out)
|
||||
downloadCount++
|
||||
mu.Unlock()
|
||||
}
|
||||
<-sem
|
||||
wg.Done()
|
||||
}(i)
|
||||
}
|
||||
wg.Wait()
|
||||
log.Printf("Scaricate %d/%d fatture\n", downloadCount, invoiceNum)
|
||||
sort.Strings(downloadedFiles)
|
||||
return downloadedFiles
|
||||
}
|
||||
|
||||
func mergeInvoices(files []string) string {
|
||||
out, err := dialog.File().Filter("PDF files", "pdf").Title("Scegli dove salvare le fatture unite").Save()
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile recuperare il file selezionato: %v\n", err)
|
||||
}
|
||||
if filepath.Ext(out) == "" {
|
||||
out += ".pdf"
|
||||
}
|
||||
err = api.MergeFile(files, out, nil)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile unire i pdf: %v\nFatture singole non rimosse\n", err)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
func openPDF(fileName string) {
|
||||
var cmd *exec.Cmd
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd = exec.Command("cmd", "/C start "+fileName)
|
||||
|
||||
} else {
|
||||
cmd = exec.Command("xdg-open", fileName)
|
||||
}
|
||||
cmd.Stdout = mw
|
||||
cmd.Stderr = mw
|
||||
err := cmd.Start()
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile aprire il pdf con le fatture unite: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
func cleanTmpDir() {
|
||||
files, err := ioutil.ReadDir(outDir)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile recuperare la lista di file creati nella directory temporanea: %v\n", err)
|
||||
}
|
||||
for _, file := range files {
|
||||
err = os.Remove(filepath.FromSlash(outDir + "/" + file.Name()))
|
||||
if err != nil {
|
||||
log.Printf("Impossibile eliminare la fattura singola %v: %v\n", file, err)
|
||||
}
|
||||
}
|
||||
err = os.Remove(outDir)
|
||||
if err != nil {
|
||||
log.Printf("Impossibile eliminare la directory temporanea %v: %v\n", outDir, err)
|
||||
}
|
||||
}
|
||||
|
||||
func minOf(vars ...int) int {
|
||||
min := vars[0]
|
||||
|
||||
for _, i := range vars {
|
||||
if min > i {
|
||||
min = i
|
||||
}
|
||||
}
|
||||
|
||||
return min
|
||||
}
|
||||
|
||||
func main() {
|
||||
exitWithError = false
|
||||
args := os.Args
|
||||
tmpDir = os.TempDir()
|
||||
|
||||
if runtime.GOOS == "linux" {
|
||||
logPath = tmpDir + "/log_fattureSanRossore.log"
|
||||
} else {
|
||||
logPath = filepath.FromSlash(tmpDir + "/log_fattureSanRossore.txt")
|
||||
}
|
||||
logFile, err := os.OpenFile(filepath.FromSlash(logPath), os.O_CREATE|os.O_TRUNC|os.O_RDWR, 0666)
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile creare il file di log: %v\n", err)
|
||||
}
|
||||
mw = io.MultiWriter(os.Stderr, logFile)
|
||||
log.SetOutput(mw)
|
||||
|
||||
defer func() {
|
||||
if !exitWithError {
|
||||
cleanTmpDir()
|
||||
} else {
|
||||
log.Println("I file temporanei non sono stati eliminati per poterli riesaminare")
|
||||
}
|
||||
log.Printf("Log file salvato in %v\n", logPath)
|
||||
}()
|
||||
|
||||
outDir = createTmpDir()
|
||||
|
||||
var fileName string
|
||||
if len(args) < 2 {
|
||||
var err error
|
||||
fileName, err = dialog.File().Filter("XLS files", "xls").Load()
|
||||
if err != nil {
|
||||
exitWithError = true
|
||||
log.Panicf("Impossibile recuperare il file selezionato: %v\n", err)
|
||||
}
|
||||
} else {
|
||||
fileName = args[1]
|
||||
}
|
||||
|
||||
filePath := checkFile(fileName)
|
||||
IDs := getInvoiceIDs(filePath)
|
||||
URLs := getInvoiceURLs(filePath)
|
||||
dlFiles := downloadInvoices(IDs, URLs)
|
||||
pdf := mergeInvoices(dlFiles)
|
||||
openPDF(pdf)
|
||||
}
|
@@ -1,12 +0,0 @@
|
||||
module github.com/Noettore/fattureSanRossore/fattureDownloader
|
||||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 // indirect
|
||||
github.com/extrame/xls v0.0.1
|
||||
github.com/gotk3/gotk3 v0.4.0 // indirect
|
||||
github.com/pdfcpu/pdfcpu v0.3.2
|
||||
github.com/sqweek/dialog v0.0.0-20200304031853-0dcd55bfe06a
|
||||
mvdan.cc/xurls/v2 v2.2.0
|
||||
)
|
@@ -1,45 +0,0 @@
|
||||
github.com/BurntSushi/freetype-go v0.0.0-20160129220410-b763ddbfe298/go.mod h1:D+QujdIlUNfa0igpNMk6UIvlb6C252URs4yupRUV4lQ=
|
||||
github.com/BurntSushi/graphics-go v0.0.0-20160129215708-b43f31a4a966/go.mod h1:Mid70uvE93zn9wgF92A/r5ixgnvX8Lh68fxp9KQBaI0=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/BurntSushi/xgbutil v0.0.0-20160919175755-f7c97cef3b4e/go.mod h1:uw9h2sd4WWHOPdJ13MQpwK5qYWKYDumDqxWWIknEQ+k=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf h1:FPsprx82rdrX2jiKyS17BH6IrTmUBYqZa/CXT4uvb+I=
|
||||
github.com/TheTitanrain/w32 v0.0.0-20180517000239-4f5cfb03fabf/go.mod h1:peYoMncQljjNS6tZwI9WVyQB3qZS6u79/N3mBOcnd3I=
|
||||
github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7 h1:n+nk0bNe2+gVbRI8WRbLFVwwcBQ0rr5p+gzkKb6ol8c=
|
||||
github.com/extrame/ole2 v0.0.0-20160812065207-d69429661ad7/go.mod h1:GPpMrAfHdb8IdQ1/R2uIRBsNfnPnwsYE9YYI5WyY1zw=
|
||||
github.com/extrame/xls v0.0.1 h1:jI7L/o3z73TyyENPopsLS/Jlekm3nF1a/kF5hKBvy/k=
|
||||
github.com/extrame/xls v0.0.1/go.mod h1:iACcgahst7BboCpIMSpnFs4SKyU9ZjsvZBfNbUxZOJI=
|
||||
github.com/frankban/quicktest v1.5.0/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03DbaAcR7Ks/o=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/gotk3/gotk3 v0.4.0 h1:TIuhyQitGeRTxOQIV3AJlYtEWWJpC74JHwAIsxlH8MU=
|
||||
github.com/gotk3/gotk3 v0.4.0/go.mod h1:Eew3QBwAOBTrfFFDmsDE5wZWbcagBL1NUslj1GhRveo=
|
||||
github.com/hhrutter/lzw v0.0.0-20190827003112-58b82c5a41cc/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
|
||||
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650 h1:1yY/RQWNSBjJe2GDCIYoLmpWVidrooriUr4QS/zaATQ=
|
||||
github.com/hhrutter/lzw v0.0.0-20190829144645-6f07a24e8650/go.mod h1:yJBvOcu1wLQ9q9XZmfiPfur+3dQJuIhYQsMGLYcItZk=
|
||||
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7 h1:o1wMw7uTNyA58IlEdDpxIrtFHTgnvYzA8sCQz8luv94=
|
||||
github.com/hhrutter/tiff v0.0.0-20190829141212-736cae8d0bc7/go.mod h1:WkUxfS2JUu3qPo6tRld7ISb8HiC0gVSU91kooBMDVok=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/mattn/go-gtk v0.0.0-20180216084204-5a311a1830ab/go.mod h1:PwzwfeB5syFHXORC3MtPylVcjIoTDT/9cvkKpEndGVI=
|
||||
github.com/mattn/go-pointer v0.0.0-20171114154726-1d30dc4b6f28/go.mod h1:2zXcozF6qYGgmsG+SeTZz3oAbFLdD3OWqnUbNvJZAlc=
|
||||
github.com/pdfcpu/pdfcpu v0.3.2 h1:oHnvW3KUed/jVLnNcN5FyJsmInXAyyfoZ4yG3mxJdk8=
|
||||
github.com/pdfcpu/pdfcpu v0.3.2/go.mod h1:/ULj8B76ZnB4445B0yuSASQqlN0kEO+khtEnmPdEoXU=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rogpeppe/go-internal v1.5.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/skelterjohn/go.wde v0.0.0-20180104102407-a0324cbf3ffe/go.mod h1:zXxNsJHeUYIqpg890APBNEn9GoCbA4Cdnvuv3mx4fBk=
|
||||
github.com/sqweek/dialog v0.0.0-20200304031853-0dcd55bfe06a h1:BHv3lo0aZg2IPfeBfgYFjq48DoKehP+JC9dtACUEmT4=
|
||||
github.com/sqweek/dialog v0.0.0-20200304031853-0dcd55bfe06a/go.mod h1:QSrNdZLZB8VoFPGlZ2vDuA2oNaVdhld3g0PZLc7soX8=
|
||||
github.com/tealeg/xlsx v1.0.5 h1:+f8oFmvY8Gw1iUXzPk+kz+4GpbDZPK1FhPiQRd+ypgE=
|
||||
github.com/tealeg/xlsx/v2 v2.0.1 h1:RP+VEscpPFjH2FnpKh1p9HVLAk1htqb9Urcxi2AU1ns=
|
||||
github.com/tealeg/xlsx/v2 v2.0.1/go.mod h1:l9GvhCCjdaIGkAyZcFedDALcYcXUOei55f6umRMOz9c=
|
||||
golang.org/x/image v0.0.0-20190823064033-3a9bac650e44/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52 h1:2fktqPPvDiVEEVT/vSTeoUPXfmRxRaGy6GU8jypvEn0=
|
||||
golang.org/x/image v0.0.0-20191214001246-9130b4cfad52/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
mvdan.cc/xurls v1.1.0 h1:kj0j2lonKseISJCiq1Tfk+iTv65dDGCl0rTbanXJGGc=
|
||||
mvdan.cc/xurls/v2 v2.2.0 h1:NSZPykBXJFCetGZykLAxaL6SIpvbVy/UFEniIfHAa8A=
|
||||
mvdan.cc/xurls/v2 v2.2.0/go.mod h1:EV1RMtya9D6G5DMYPGD8zTQzaHet6Jh8gFlRgGRJeO8=
|
@@ -1,56 +0,0 @@
|
||||
# Manuale operativo `fattureSanRossore.exe`
|
||||
|
||||
L’utility `fattureSanRossore` ha come unico obiettivo quello di scaricare automaticamente le fatture emesse dalla Casa di Cura San Rossore per conto di un medico.
|
||||
|
||||
Per utilizzarla è necessario essere in possesso di un file xls (Microsoft Excel) generato e scaricato dal portale `STAT_FATTURATO_CTERZI` raggiungibile qui:
|
||||
|
||||
[https://report.casadicurasanrossore.it:8443/Reports/Pages/Folder.aspx](https://report.casadicurasanrossore.it:8443/Reports/Pages/Folder.aspx).
|
||||
|
||||
Ecco in dettaglio la procedura da seguire
|
||||
|
||||
## 1. Download del report in formato `xls`
|
||||
|
||||
Al termine di questa procedura avremo a disposizione il report in formato `xls` delle fatture emesse dalla Casa di Cura San Rossore per conto terzi. Il report verrà poi utilizzato nella fase seguente per il download delle fatture.
|
||||
|
||||
1. Aprire l’indirizzo del portale di generazione del report:
|
||||
|
||||
[https://report.casadicurasanrossore.it:8443/Reports/Pages/Folder.aspx](https://report.casadicurasanrossore.it:8443/Reports/Pages/Folder.aspx)
|
||||
|
||||
2. Effettuare il login con le credenziali del medico accreditato
|
||||
|
||||
3. Nella pagina fare click su `STAT_FATTURATO_CTERZI` per aprire la procedura di generazione dei report
|
||||
|
||||

|
||||
|
||||
4. Modificare le date nei campi `data dal` e `data al` in modo che rispecchino il periodo di interesse
|
||||
|
||||
5. Fare click sul pulsante `Visualizza report` presente nella parte alta della pagina, sulla destra
|
||||
|
||||

|
||||
|
||||
6. Fare click sul pulsante raffigurante l’icona di salvataggio
|
||||
|
||||
7. Selezionare l’opzione `Excel` nel menù a tendina
|
||||
|
||||

|
||||
|
||||
8. Salvare il file in una posizione nota
|
||||
|
||||
## 2. Avvio della utility e download delle fatture
|
||||
|
||||
1. Avviare la utility facendo doppio click sull’eseguibile `fattureSanRossore-win-0.1.exe`
|
||||
|
||||
2. Nella finestra di dialogo selezionare il report in formato `xls` generato nella fase precedente
|
||||
|
||||

|
||||
|
||||
3. Attendere il completamento dei download
|
||||
|
||||

|
||||
|
||||
4. Nella finestra di dialogo selezionare la cartella e dare un nome al file `pdf` che conterrà le fatture scaricate
|
||||
|
||||

|
||||
|
||||
5. Si aprirà automaticamente il lettore pdf con il file generato
|
||||
|
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 14 KiB |
Binary file not shown.
Before Width: | Height: | Size: 60 KiB |
Binary file not shown.
Before Width: | Height: | Size: 65 KiB |
Binary file not shown.
Before Width: | Height: | Size: 182 KiB |
Binary file not shown.
Before Width: | Height: | Size: 314 KiB |
Binary file not shown.
Before Width: | Height: | Size: 230 KiB |
0
fatture_ccsr/__init__.py
Normal file
0
fatture_ccsr/__init__.py
Normal file
99
fatture_ccsr/downloader.py
Normal file
99
fatture_ccsr/downloader.py
Normal file
@@ -0,0 +1,99 @@
|
||||
"""ask for an input file (.xlsx) and an output file (.pdf) and downloads and unite every invoice"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
import tempfile
|
||||
import openpyxl
|
||||
import PyPDF2
|
||||
import wx
|
||||
|
||||
def get_invoices_info(input_file_path: str) -> tuple:
|
||||
"""extract invoices IDs and URLs from xlsx input file"""
|
||||
xlsx_file = openpyxl.load_workbook(input_file_path)
|
||||
sheet = xlsx_file.active
|
||||
invoices = dict()
|
||||
|
||||
owner_name = '_'.join(sheet["B1"].value.split()[2:])
|
||||
|
||||
for i in range(1, sheet.max_row+1):
|
||||
invoice_id = sheet["I"+str(i)].value
|
||||
if invoice_id is not None and "CCSR" in invoice_id:
|
||||
invoice_id = invoice_id.replace("/", "-")
|
||||
invoice_url = sheet["BG"+str(i)].hyperlink.target
|
||||
invoice = {
|
||||
"id": invoice_id,
|
||||
"url": invoice_url,
|
||||
"path": None,
|
||||
"good": None,
|
||||
}
|
||||
invoices[invoice_id] = invoice
|
||||
invoices_info = (owner_name, invoices)
|
||||
return invoices_info
|
||||
|
||||
def open_file(file_path):
|
||||
"""open a file with the default software"""
|
||||
if sys.platform == "win32":
|
||||
os.startfile(file_path) # pylint: disable=maybe-no-member
|
||||
else:
|
||||
opener = "open" if sys.platform == "darwin" else "xdg-open"
|
||||
subprocess.call([opener, file_path])
|
||||
|
||||
def download_invoices(parent):
|
||||
"""download invoices from CCSR"""
|
||||
output_file_path = None
|
||||
|
||||
invoices_info = get_invoices_info(parent.input_file_path)
|
||||
invoices = invoices_info[1]
|
||||
|
||||
parent.log_dialog.log_text.AppendText("Inizio download fatture dal portale CCSR\n")
|
||||
wx.Yield()
|
||||
|
||||
tmp_dir = tempfile.mkdtemp()
|
||||
|
||||
invoices_count = len(invoices)
|
||||
downloaded_count = 0
|
||||
|
||||
for invoice_id, invoice in invoices.items():
|
||||
resp = parent.session.get(invoice["url"])
|
||||
if resp.status_code == 200:
|
||||
with open(tmp_dir+"/"+invoice_id+".pdf", "wb") as output_file:
|
||||
output_file.write(resp.content)
|
||||
invoice["path"] = output_file.name
|
||||
try:
|
||||
PyPDF2.PdfFileReader(open(invoice["path"], "rb"))
|
||||
except (PyPDF2.utils.PdfReadError, OSError):
|
||||
parent.log_dialog.log_text.AppendText("Errore: fattura %s corrotta!\n" % invoice_id)
|
||||
wx.Yield()
|
||||
invoice["good"] = False
|
||||
else:
|
||||
downloaded_count += 1
|
||||
parent.log_dialog.log_text.AppendText("%d/%d scaricata fattura %s in %s\n" % (downloaded_count, invoices_count, invoice_id, invoice["path"]))
|
||||
wx.Yield()
|
||||
invoice["good"] = True
|
||||
else:
|
||||
parent.log_dialog.log_text.AppendText("Errore: impossibile scaricare fattura %s: %d\n" % (invoice_id, resp.status_code))
|
||||
wx.Yield()
|
||||
invoice["good"] = False
|
||||
|
||||
parent.output_pdf_dialog.SetFilename("fatture_%s.pdf" % invoices_info[0])
|
||||
|
||||
if parent.output_pdf_dialog.ShowModal() == wx.ID_OK:
|
||||
output_file_path = parent.output_pdf_dialog.GetPath()
|
||||
else:
|
||||
#TODO: avviso errore file output
|
||||
return
|
||||
|
||||
merger = PyPDF2.PdfFileMerger()
|
||||
for invoice in invoices.values():
|
||||
if invoice["good"]:
|
||||
merger.append(PyPDF2.PdfFileReader(open(invoice["path"], "rb")))
|
||||
merger.write(output_file_path)
|
||||
|
||||
open_file(output_file_path)
|
||||
|
||||
shutil.rmtree(tmp_dir, ignore_errors=True)
|
||||
|
||||
parent.log_dialog.log_text.AppendText("Download terminato.\nIl pdf contenente le fatture si trova in %s\n" % output_file_path)
|
||||
wx.Yield()
|
41
fatture_ccsr/exc.py
Normal file
41
fatture_ccsr/exc.py
Normal file
@@ -0,0 +1,41 @@
|
||||
"""Define Python user-defined exceptions"""
|
||||
|
||||
class FattureSanRossoreError(Exception):
|
||||
"""Base class for other exceptions"""
|
||||
|
||||
class FileError(FattureSanRossoreError):
|
||||
"""Basic exception for errors raised by files"""
|
||||
def __init__(self, file_path, msg=None):
|
||||
if msg is None:
|
||||
msg = "An error occurred with file %s" % file_path
|
||||
super(FileError, self).__init__(msg)
|
||||
self.file_path = file_path
|
||||
|
||||
class NoFileExtensionError(FileError):
|
||||
"""Raised when a file has no exception"""
|
||||
def __init__(self, file_path):
|
||||
super(NoFileExtensionError, self).__init__(file_path, msg="File %s has no extension!" % file_path)
|
||||
class WrongFileExtensionError(FileError):
|
||||
"""Raised when a file extension is not accepted"""
|
||||
def __init__(self, file_path, file_ext, allowed_ext):
|
||||
super(WrongFileExtensionError, self).__init__(file_path, msg="Cannot accept file %s extension %s. Allowed extensions are %s" % (file_path, file_ext, allowed_ext))
|
||||
self.file_ext = file_ext
|
||||
|
||||
class NoFileError(FileError):
|
||||
"""Raised when file_path is None or an empty string"""
|
||||
def __init__(self):
|
||||
super(NoFileError, self).__init__(None, msg="Not setted or empty file path!")
|
||||
|
||||
|
||||
class ActionError(FattureSanRossoreError):
|
||||
"""Basic exception for errors raised by actions"""
|
||||
def __init__(self, action, msg=None):
|
||||
if msg is None:
|
||||
msg = "An error occurred with %s action" % action
|
||||
super(ActionError, self).__init__(msg)
|
||||
self.action = action
|
||||
|
||||
class InvalidActionError(ActionError):
|
||||
"""Raised when an invalid action is used"""
|
||||
def __init__(self, action):
|
||||
super(InvalidActionError, self).__init__(action, "Invalid action %s" % action)
|
303
fatture_ccsr/fatture_ccsr.py
Normal file
303
fatture_ccsr/fatture_ccsr.py
Normal file
@@ -0,0 +1,303 @@
|
||||
"""This utility is used for downloading or converting to TRAF2000 invoices from a CCSR .xlsx, .csv or .xml report file"""
|
||||
|
||||
import os
|
||||
import tempfile
|
||||
import atexit
|
||||
import wx
|
||||
import wx.adv
|
||||
import requests
|
||||
import requests_ntlm
|
||||
|
||||
import downloader
|
||||
import traf2000_converter
|
||||
import exc
|
||||
|
||||
LOGIN_ACTION = 0
|
||||
LOGOUT_ACTION = 1
|
||||
DOWNLOAD_ACTION = 10
|
||||
CONVERT_ACTION = 20
|
||||
|
||||
def file_extension(file_path: str, allowed_ext: set = None) -> str:
|
||||
"""Return the file extension if that's in the allowed extension set"""
|
||||
if file_path in (None, ""):
|
||||
raise exc.NoFileError()
|
||||
file_ext = os.path.splitext(file_path)[1]
|
||||
if file_ext in (None, ""):
|
||||
raise exc.NoFileExtensionError
|
||||
if allowed_ext is not None and file_ext not in allowed_ext:
|
||||
raise exc.WrongFileExtensionError
|
||||
return file_ext
|
||||
|
||||
class LogDialog(wx.Dialog):
|
||||
"""logging panel"""
|
||||
def __init__(self, parent, title, action):
|
||||
super(LogDialog, self).__init__(parent, wx.ID_ANY, title, style=wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER)
|
||||
|
||||
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
self.log_text = wx.TextCtrl(self, wx.ID_ANY, size=(500, 200), style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL|wx.EXPAND)
|
||||
log_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
log_sizer.Add(self.log_text, 1, wx.ALL|wx.EXPAND, 2)
|
||||
|
||||
self.log_text.Bind(wx.EVT_TEXT, self.on_text_update)
|
||||
|
||||
if action == CONVERT_ACTION:
|
||||
self.nc_text = wx.TextCtrl(self, wx.ID_ANY, size=(300, 200), style=wx.TE_MULTILINE|wx.TE_READONLY|wx.HSCROLL)
|
||||
self.nc_text.Bind(wx.EVT_TEXT, self.on_text_update)
|
||||
log_sizer.Add(self.nc_text, 1, wx.ALL|wx.EXPAND, 2)
|
||||
|
||||
main_sizer.Add(log_sizer, 1, wx.ALL|wx.EXPAND, 2)
|
||||
self.btn = wx.Button(self, wx.ID_OK, "Chiudi")
|
||||
self.btn.Disable()
|
||||
main_sizer.Add(self.btn, 0, wx.ALL|wx.CENTER, 2)
|
||||
|
||||
self.SetSizer(main_sizer)
|
||||
main_sizer.Fit(self)
|
||||
|
||||
self.Layout()
|
||||
|
||||
def on_text_update(self, event):
|
||||
"""autoscroll on text update"""
|
||||
self.ScrollPages(-1)
|
||||
event.Skip()
|
||||
|
||||
class LoginDialog(wx.Dialog):
|
||||
"""login dialog for basic auth download"""
|
||||
def __init__(self, *args, **kwds):
|
||||
"""constructor"""
|
||||
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_DIALOG_STYLE
|
||||
wx.Dialog.__init__(self, *args, **kwds)
|
||||
self.SetTitle("Login")
|
||||
|
||||
self.logged_in = False
|
||||
|
||||
main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
user_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
main_sizer.Add(user_sizer, 1, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
user_lbl = wx.StaticText(self, wx.ID_ANY, "Username:")
|
||||
user_sizer.Add(user_lbl, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)
|
||||
|
||||
self.username = wx.TextCtrl(self, wx.ID_ANY, "")
|
||||
self.username.SetMinSize((250, -1))
|
||||
user_sizer.Add(self.username, 0, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
pass_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
main_sizer.Add(pass_sizer, 1, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
pass_lbl = wx.StaticText(self, wx.ID_ANY, "Password:")
|
||||
pass_sizer.Add(pass_lbl, 0, wx.ALIGN_CENTER_VERTICAL | wx.ALL, 2)
|
||||
|
||||
self.password = wx.TextCtrl(self, wx.ID_ANY, "", style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER)
|
||||
self.password.SetMinSize((250, -1))
|
||||
pass_sizer.Add(self.password, 0, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
self.login_btn = wx.Button(self, wx.ID_ANY, "Login")
|
||||
self.login_btn.SetFocus()
|
||||
self.login_btn.Bind(wx.EVT_BUTTON, self.on_login)
|
||||
main_sizer.Add(self.login_btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
|
||||
|
||||
self.SetSizer(main_sizer)
|
||||
main_sizer.Fit(self)
|
||||
|
||||
self.Layout()
|
||||
|
||||
def disconnect(self):
|
||||
"""close session and reset input fields"""
|
||||
self.GetParent().session.close()
|
||||
self.logged_in = False
|
||||
|
||||
def on_login(self, _):
|
||||
"""check credentials and login"""
|
||||
if self.username.GetValue() not in ("", None) and self.password.GetValue() not in ("", None):
|
||||
session = self.GetParent().session
|
||||
session.auth = requests_ntlm.HttpNtlmAuth("sr\\"+self.username.GetValue(), self.password.GetValue())
|
||||
if session.get('https://report.casadicurasanrossore.it:8443/Reports/browse/').status_code == 200:
|
||||
self.logged_in = True
|
||||
self.username.SetValue('')
|
||||
self.password.SetValue('')
|
||||
self.Close()
|
||||
|
||||
class FattureCCSRFrame(wx.Frame):
|
||||
"""main application frame"""
|
||||
def __init__(self, *args, **kwds):
|
||||
atexit.register(self.exit_handler)
|
||||
|
||||
kwds["style"] = kwds.get("style", 0) | wx.DEFAULT_FRAME_STYLE | wx.FULL_REPAINT_ON_RESIZE | wx.TAB_TRAVERSAL
|
||||
wx.Frame.__init__(self, *args, **kwds)
|
||||
self.SetTitle("fattureCCSR")
|
||||
|
||||
self._initial_locale = wx.Locale(wx.LANGUAGE_DEFAULT, wx.LOCALE_LOAD_DEFAULT)
|
||||
|
||||
self.input_file_path = None
|
||||
self.input_file_ext = None
|
||||
self.input_files = list()
|
||||
self.log_dialog = None
|
||||
self.session = requests.Session()
|
||||
|
||||
self.panel = wx.Panel(self, wx.ID_ANY, style=wx.BORDER_NONE | wx.FULL_REPAINT_ON_RESIZE | wx.TAB_TRAVERSAL)
|
||||
self.main_sizer = wx.BoxSizer(wx.VERTICAL)
|
||||
|
||||
title_lbl = wx.StaticText(self.panel, wx.ID_ANY, "Utility Fatture Casa di Cura San Rossore")
|
||||
title_lbl.SetFont(wx.Font(10, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_BOLD, 0, ""))
|
||||
self.main_sizer.Add(title_lbl, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
|
||||
|
||||
desc_lbl = wx.StaticText(self.panel, wx.ID_ANY, "Effettua il login poi seleziona le date di inizio e fine periodo delle fatture da gestire ed esegui un'azione")
|
||||
self.main_sizer.Add(desc_lbl, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
|
||||
|
||||
self.login_dlg = LoginDialog(self)
|
||||
self.output_traf2000_dialog = wx.FileDialog(self.panel, "Scegli dove salvare il file TRAF2000", defaultFile="TRAF2000", style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
|
||||
self.output_pdf_dialog = wx.FileDialog(self.panel, "Scegli dove salvare il .pdf con le fatture scaricate", defaultFile="fatture.pdf", style=wx.FD_SAVE|wx.FD_OVERWRITE_PROMPT)
|
||||
|
||||
self.login_btn = wx.Button(self.panel, LOGIN_ACTION, "Login")
|
||||
self.login_btn.SetFocus()
|
||||
self.login_btn.Bind(wx.EVT_BUTTON, self.btn_onclick)
|
||||
self.main_sizer.Add(self.login_btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
|
||||
|
||||
self.logout_btn = wx.Button(self.panel, LOGOUT_ACTION, "Logout")
|
||||
self.logout_btn.Hide()
|
||||
self.logout_btn.Bind(wx.EVT_BUTTON, self.btn_onclick)
|
||||
self.main_sizer.Add(self.logout_btn, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
|
||||
|
||||
date_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.main_sizer.Add(date_sizer, 0, wx.ALL | wx.EXPAND, 2)
|
||||
|
||||
start_date_lbl = wx.StaticText(self.panel, wx.ID_ANY, "Dal")
|
||||
date_sizer.Add(start_date_lbl, 0, wx.ALL, 2)
|
||||
|
||||
self.start_date_picker = wx.adv.DatePickerCtrl(self.panel, wx.ID_ANY, dt=wx.DateTime.Today().SetDay(1))
|
||||
self.start_date_picker.Enable(False)
|
||||
date_sizer.Add(self.start_date_picker, 1, wx.ALL, 2)
|
||||
|
||||
end_date_lbl = wx.StaticText(self.panel, wx.ID_ANY, "Al")
|
||||
date_sizer.Add(end_date_lbl, 0, wx.ALL, 2)
|
||||
|
||||
self.end_date_picker = wx.adv.DatePickerCtrl(self.panel, wx.ID_ANY, dt=wx.DateTime.Today())
|
||||
self.end_date_picker.Enable(False)
|
||||
date_sizer.Add(self.end_date_picker, 1, wx.ALL, 2)
|
||||
|
||||
action_sizer = wx.BoxSizer(wx.HORIZONTAL)
|
||||
self.main_sizer.Add(action_sizer, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.ALL, 2)
|
||||
|
||||
self.download_btn = wx.Button(self.panel, DOWNLOAD_ACTION, "Scarica Fatture")
|
||||
self.download_btn.Enable(False)
|
||||
self.download_btn.Bind(wx.EVT_BUTTON, self.btn_onclick)
|
||||
action_sizer.Add(self.download_btn, 0, wx.ALL, 2)
|
||||
|
||||
self.traf2000_btn = wx.Button(self.panel, CONVERT_ACTION, "Genera TRAF2000")
|
||||
self.traf2000_btn.Enable(False)
|
||||
self.traf2000_btn.Bind(wx.EVT_BUTTON, self.btn_onclick)
|
||||
action_sizer.Add(self.traf2000_btn, 0, wx.ALL, 2)
|
||||
|
||||
self.panel.SetSizer(self.main_sizer)
|
||||
|
||||
self.main_sizer.Fit(self)
|
||||
self.Layout()
|
||||
self.Centre()
|
||||
|
||||
def enable_on_login(self):
|
||||
"""enable and show what needed after login"""
|
||||
self.download_btn.Enable()
|
||||
self.traf2000_btn.Enable()
|
||||
self.start_date_picker.Enable()
|
||||
self.end_date_picker.Enable()
|
||||
self.login_btn.Hide()
|
||||
self.logout_btn.Show()
|
||||
self.main_sizer.Layout()
|
||||
|
||||
def disable_on_logout(self):
|
||||
"""disable and hide what needed after logout"""
|
||||
self.download_btn.Disable()
|
||||
self.traf2000_btn.Disable()
|
||||
self.start_date_picker.Disable()
|
||||
self.end_date_picker.Disable()
|
||||
self.login_dlg.disconnect()
|
||||
self.logout_btn.Hide()
|
||||
self.login_btn.Show()
|
||||
self.main_sizer.Layout()
|
||||
|
||||
def btn_onclick(self, event):
|
||||
"""event raised when a button is clicked"""
|
||||
btn_id = event.GetEventObject().GetId()
|
||||
|
||||
if btn_id not in (LOGIN_ACTION, LOGOUT_ACTION, DOWNLOAD_ACTION, CONVERT_ACTION):
|
||||
#TODO: error
|
||||
return
|
||||
elif btn_id == LOGIN_ACTION:
|
||||
self.login_dlg.ShowModal()
|
||||
if self.login_dlg.logged_in:
|
||||
self.enable_on_login()
|
||||
return
|
||||
elif btn_id == LOGOUT_ACTION:
|
||||
self.disable_on_logout()
|
||||
return
|
||||
elif not self.login_dlg.logged_in:
|
||||
#TODO: error
|
||||
return
|
||||
|
||||
start_date = self.start_date_picker.GetValue().Format("%d/%m/%Y")
|
||||
end_date = self.end_date_picker.GetValue().Format("%d/%m/%Y")
|
||||
input_file_url = 'https://report.casadicurasanrossore.it:8443/reportserver?/STAT_FATTURATO_CTERZI&dataI='+start_date+'&dataF='+end_date+'&rs:Format='
|
||||
input_file_url += ('EXCELOPENXML' if btn_id == DOWNLOAD_ACTION else 'XML' if btn_id == CONVERT_ACTION else None)
|
||||
|
||||
downloaded_input_file = self.session.get(input_file_url)
|
||||
if downloaded_input_file.status_code != 200:
|
||||
#TODO: error
|
||||
return
|
||||
|
||||
input_file_descriptor, self.input_file_path = tempfile.mkstemp(suffix=('.xlsx' if btn_id == DOWNLOAD_ACTION else '.xml' if btn_id == CONVERT_ACTION else None))
|
||||
self.input_files.append(self.input_file_path)
|
||||
with open(input_file_descriptor, 'wb') as input_file:
|
||||
input_file.write(downloaded_input_file.content)
|
||||
|
||||
try:
|
||||
self.input_file_ext = file_extension(self.input_file_path, (".xml", ".csv", ".xlsx"))
|
||||
except exc.NoFileError as handled_exception:
|
||||
print(handled_exception.args[0])
|
||||
return
|
||||
except exc.NoFileExtensionError as handled_exception:
|
||||
print(handled_exception.args[0])
|
||||
return
|
||||
except exc.WrongFileExtensionError as handled_exception:
|
||||
print(handled_exception.args[0])
|
||||
return
|
||||
|
||||
if btn_id == DOWNLOAD_ACTION:
|
||||
self.log_dialog = LogDialog(self, "Download delle fatture dal portale CCSR", DOWNLOAD_ACTION)
|
||||
self.log_dialog.Show()
|
||||
downloader.download_invoices(self)
|
||||
self.log_dialog.btn.Enable()
|
||||
|
||||
elif btn_id == CONVERT_ACTION:
|
||||
self.log_dialog = LogDialog(self, "Conversione delle fatture in TRAF2000", CONVERT_ACTION)
|
||||
self.log_dialog.Show()
|
||||
#TODO: error frame
|
||||
try:
|
||||
traf2000_converter.convert(self)
|
||||
except exc.NoFileError as handled_exception:
|
||||
print(handled_exception.args[0])
|
||||
except exc.NoFileExtensionError as handled_exception:
|
||||
print(handled_exception.args[0])
|
||||
except exc.WrongFileExtensionError as handled_exception:
|
||||
print(handled_exception.args[0])
|
||||
self.log_dialog.btn.Enable()
|
||||
|
||||
def exit_handler(self):
|
||||
"""clean the environment befor exiting"""
|
||||
for input_file in self.input_files:
|
||||
os.remove(input_file)
|
||||
|
||||
|
||||
class FattureCCSR(wx.App):
|
||||
"""main app"""
|
||||
def OnInit(self): # pylint: disable=invalid-name
|
||||
"""execute on app initialization"""
|
||||
self.fatture_ccsr_frame = FattureCCSRFrame(None, wx.ID_ANY, "") # pylint: disable=attribute-defined-outside-init
|
||||
self.SetTopWindow(self.fatture_ccsr_frame)
|
||||
self.fatture_ccsr_frame.Show()
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
fattureCCSR = FattureCCSR(0)
|
||||
fattureCCSR.MainLoop()
|
299
fatture_ccsr/traf2000_converter.py
Executable file
299
fatture_ccsr/traf2000_converter.py
Executable file
@@ -0,0 +1,299 @@
|
||||
"""ask for an input file and an output file and generates the TRAF2000 records from a .csv or .xml"""
|
||||
|
||||
import datetime
|
||||
import csv
|
||||
import xml.etree.ElementTree
|
||||
import unidecode
|
||||
import wx
|
||||
|
||||
def import_csv(parent) -> dict:
|
||||
"""Return a dict containing the invoices info"""
|
||||
invoices = dict()
|
||||
with open(parent.input_file_path, newline="") as csv_file:
|
||||
csv_reader = csv.reader(csv_file, delimiter=",")
|
||||
|
||||
for _ in range(4):
|
||||
next(csv_reader)
|
||||
|
||||
for line in csv_reader:
|
||||
if len(line) == 0:
|
||||
break
|
||||
invoice_num = line[1]
|
||||
invoice_type = line[8]
|
||||
amount = line[15]
|
||||
sign = 1
|
||||
if invoice_type == "Nota di credito" and '(' not in amount:
|
||||
sign = -1
|
||||
amount = int(line[15].replace("€", "").replace(",", "").replace(".", "").replace("(", "").replace(")", "")) * sign
|
||||
if invoice_num not in invoices:
|
||||
invoice = {
|
||||
"numFattura": invoice_num,
|
||||
"tipoFattura": invoice_type,
|
||||
"rifFattura": line[4],
|
||||
"dataFattura": line[2].replace("/", ""),
|
||||
"ragioneSociale": unidecode.unidecode(line[6] + " " + " ".join(line[5].split()[0:2])),
|
||||
"posDivide": str(len(line[6]) + 1),
|
||||
"cf": line[7],
|
||||
"importoTotale": 0,
|
||||
"ritenutaAcconto": 0,
|
||||
"righe": dict()
|
||||
}
|
||||
invoices[invoice_num] = invoice
|
||||
|
||||
if line[14] == "Ritenuta d'acconto":
|
||||
invoices[invoice_num]["ritenutaAcconto"] = amount
|
||||
|
||||
else:
|
||||
invoices[invoice_num]["importoTotale"] += amount
|
||||
invoices[invoice_num]["righe"][line[14]] = amount
|
||||
parent.log_dialog.log_text.AppendText("Importata fattura n. %s\n" % invoice_num)
|
||||
wx.Yield()
|
||||
return invoices
|
||||
|
||||
def import_xml(parent) -> dict:
|
||||
"""Return a dict containing the invoices info"""
|
||||
invoices = dict()
|
||||
|
||||
tree = xml.etree.ElementTree.parse(parent.input_file_path)
|
||||
root = tree.getroot()
|
||||
|
||||
for invoice in root.iter('{STAT_FATTURATO_CTERZI}Dettagli'):
|
||||
lines = dict()
|
||||
invoice_num = invoice.get('protocollo_fatturatestata')
|
||||
invoice_type = invoice.get('fat_ndc')
|
||||
total_amount = 0
|
||||
ritenuta_acconto = 0
|
||||
|
||||
for line in invoice.iter('{STAT_FATTURATO_CTERZI}Dettagli2'):
|
||||
desc = line.get('descrizione_fatturariga1')
|
||||
sign = 1
|
||||
if invoice_type == 'Nota di credito' and '-' not in line.get('prezzounitario_fatturariga1'):
|
||||
sign = -1
|
||||
amount = int(format(round(float(line.get('prezzounitario_fatturariga1')), 2), '.2f').replace('.', '').replace('-', '')) * sign
|
||||
if desc == "Ritenuta d'acconto":
|
||||
ritenuta_acconto = amount
|
||||
else:
|
||||
lines[desc] = amount
|
||||
total_amount += amount
|
||||
|
||||
invoice_elem = {
|
||||
"numFattura": invoice_num,
|
||||
"tipoFattura": invoice_type,
|
||||
"rifFattura": invoice.get('protocollo_fatturatestata1'),
|
||||
"dataFattura": datetime.datetime.fromisoformat(invoice.get('data_fatturatestata')).strftime("%d%m%Y"),
|
||||
"ragioneSociale": unidecode.unidecode(invoice.get('cognome_cliente') + ' ' + ' '.join(invoice.get('nome_cliente').split()[0:2])),
|
||||
"posDivide": str(len(invoice.get('cognome_cliente')) + 1),
|
||||
"cf": invoice.get('cf_piva_cliente'),
|
||||
"importoTotale": total_amount,
|
||||
"ritenutaAcconto": ritenuta_acconto,
|
||||
"righe": lines,
|
||||
}
|
||||
invoices[invoice_num] = invoice_elem
|
||||
parent.log_dialog.log_text.AppendText("Importata fattura n. %s\n" % invoice_num)
|
||||
wx.Yield()
|
||||
return invoices
|
||||
|
||||
|
||||
def convert(parent):
|
||||
"""Output to a file the TRAF2000 records"""
|
||||
output_file_path = None
|
||||
|
||||
if parent.input_file_ext == ".csv":
|
||||
invoices = import_csv(parent)
|
||||
|
||||
elif parent.input_file_ext == ".xml":
|
||||
invoices = import_xml(parent)
|
||||
|
||||
if parent.output_traf2000_dialog.ShowModal() == wx.ID_OK:
|
||||
output_file_path = parent.output_traf2000_dialog.GetPath()
|
||||
else:
|
||||
#TODO: avviso errore file output
|
||||
return
|
||||
|
||||
with open(output_file_path, "w") as traf2000_file:
|
||||
parent.log_dialog.nc_text.AppendText("Note di credito:\n")
|
||||
wx.Yield()
|
||||
|
||||
for invoice in invoices.values():
|
||||
if invoice["tipoFattura"] != "Fattura" and invoice["tipoFattura"] != "Nota di credito":
|
||||
parent.log_dialog.log_text.AppendText("Errore: il documento %s può essere FATTURA o NOTA DI CREDITO\n" % invoice["numFattura"])
|
||||
wx.Yield()
|
||||
continue
|
||||
|
||||
if len(invoice["cf"]) != 16 and len(invoice["cf"]) == 11:
|
||||
parent.log_dialog.log_text.AppendText("Errore: il documento %s non ha cf/piva\n" % invoice["numFattura"])
|
||||
wx.Yield()
|
||||
continue
|
||||
|
||||
if invoice["tipoFattura"] == "Nota di credito":
|
||||
# As for now this script doesn't handle "Note di credito"
|
||||
parent.log_dialog.nc_text.AppendText(invoice["numFattura"]+"\n")
|
||||
wx.Yield()
|
||||
continue
|
||||
|
||||
line = ["04103", "3", "0", "00000"] # TRF-DITTA + TRF-VERSIONE + TRF-TARC + TRF-COD-CLIFOR
|
||||
line.append(invoice["ragioneSociale"][:32]+' '*(32-len(invoice["ragioneSociale"]))) # TRF-RASO
|
||||
line.append(' '*30) # TRF-IND
|
||||
line.append('00000') # TRF-CAP
|
||||
line.append(' '*27) # TRF-CITTA + TRF-PROV
|
||||
|
||||
if len(invoice["cf"]) == 16: # se c.f. presente
|
||||
line.append(invoice["cf"]) # TRF-COFI
|
||||
line.append('0'*11) # TRF-PIVA
|
||||
line.append('S') # TRF-PF
|
||||
else: # se piva presente
|
||||
line.append(' '*16) # TRF-COFI
|
||||
line.append(invoice["cf"]) # TRF-PIVA
|
||||
line.append('N') # TRF-PF
|
||||
|
||||
line.append('0'*(2-len(invoice["posDivide"])) + invoice["posDivide"]) # TRF-DIVIDE
|
||||
line.append('0000') # TRF-PAESE
|
||||
line.append(' '*33) # TRF-PIVA-ESTERO + TRF-COFI-ESTERO + TRF-SESSO
|
||||
line.append('0'*8) # TRF-DTNAS
|
||||
line.append(' '*64) # TRF-COMNA + TRF-PRVNA + TRF-PREF + TRF-NTELE-NUM + TRF-FAX-PREF + TRF-FAX-NUM
|
||||
line.append('0'*22) # TRF-CFCONTO + TRF-CFCODPAG + TRF-CFBANCA + TRF-CFAGENZIA + TRF-CFINTERM
|
||||
|
||||
if invoice["tipoFattura"] == "Fattura":
|
||||
line.append('001') # TRF-CAUSALE
|
||||
line.append("FATTURA VENDITA") # TRF-CAU-DES
|
||||
else:
|
||||
line.append('002') # TRF-CAUSALE
|
||||
line.append("N.C. A CLIENTE ") # TRF-CAU-DES
|
||||
|
||||
line.append(' '*86) # TRF-CAU-AGG + TRF-CAU-AGG-1 + TRF-CAU-AGG-2
|
||||
line.append(invoice["dataFattura"]*2) # TRF-DATA-REGISTRAZIONE + TRF-DATA-DOC
|
||||
line.append('00000000') # TRF-NUM-DOC-FOR
|
||||
line.append(invoice["numFattura"][4:9]) # TRF-NDOC
|
||||
line.append('00') # TRF-SERIE
|
||||
line.append('0'*72) # TRF-EC-PARTITA + TRF-EC-PARTITA-ANNO + TRF-EC-COD-VAL + TRF-EC-CAMBIO + TRF-EC-DATA-CAMBIO + TRF-EC-TOT-DOC-VAL + TRF-EC-TOT-IVA-VAL + TRF-PLAFOND
|
||||
|
||||
count = 0
|
||||
for desc, imponibile in invoice["righe"].items():
|
||||
count += 1
|
||||
imponibile = str(imponibile)
|
||||
if '-' in imponibile:
|
||||
imponibile.replace('-', '')
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "-"
|
||||
else:
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "+"
|
||||
line.append(imponibile) # TRF-IMPONIB
|
||||
if desc != "Bollo":
|
||||
line.append('308') # TRF-ALIQ
|
||||
else:
|
||||
line.append('315') # TRF-ALIQ
|
||||
line.append('0'*16) # TRF-ALIQ-AGRICOLA + TRF-IVA11 + TRF-IMPOSTA
|
||||
|
||||
for _ in range(8-count):
|
||||
line.append('0'*31)
|
||||
|
||||
total = str(invoice["importoTotale"])
|
||||
if '-' in total:
|
||||
total.replace('-', '')
|
||||
total = '0'*(11-len(total)) + total + "-"
|
||||
else:
|
||||
total = '0'*(11-len(total)) + total + "+"
|
||||
line.append(total) # TRF-TOT-FAT
|
||||
|
||||
count = 0
|
||||
for desc, imponibile in invoice["righe"].items():
|
||||
count += 1
|
||||
imponibile = str(imponibile)
|
||||
if '-' in imponibile:
|
||||
imponibile.replace('-', '')
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "-"
|
||||
else:
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "+"
|
||||
if desc != "Bollo":
|
||||
line.append('4004300') # TRF-CONTO-RIC
|
||||
else:
|
||||
line.append('4004500') # TRF-CONTO-RIC
|
||||
line.append(imponibile) # TRF-IMP-RIC
|
||||
|
||||
for _ in range(8-count):
|
||||
line.append('0'*19)
|
||||
|
||||
line.append('000') # TRF-CAU-PAG
|
||||
line.append(' '*83) # TRF-CAU-DES-PAGAM + TRF-CAU-AGG-1-PAGAM + TRF-CAU-AGG-2-PAGAM
|
||||
line.append(('0000000' + ' ' + '0'*12 + ' '*18 + '0'*26)*80) # TRF-CONTO + TRF-DA + TRF-IMPORTO + TRF-CAU-AGGIUNT + TRF-EC-PARTITA-PAG + TRF-EC-PARTITA-ANNO-PAG + TRF-EC-IMP-VAL
|
||||
line.append((' ' + '0'*18)*10) # TRF-RIFER-TAB + TRF-IND-RIGA + TRF-DT-INI + TRF-DT-FIN
|
||||
line.append('000000') # TRF-DOC6
|
||||
line.append('N' + '0') # TRF-AN-OMONIMI + TRF-AN-TIPO-SOGG
|
||||
line.append('00'*80) # TRF-EC-PARTITA-SEZ-PAG
|
||||
line.append('0'*15) # TRF-NUM-DOC-PAG-PROF + TRF-DATA-DOC-PAG-PROF
|
||||
|
||||
if invoice["ritenutaAcconto"] != 0:
|
||||
imponibile = str(invoice["ritenutaAcconto"])
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "+"
|
||||
line.append(imponibile) # TRF-RIT-ACC
|
||||
else:
|
||||
line.append('0'*12) # TRF-RIT-ACC
|
||||
|
||||
line.append('0'*60) # TRF-RIT-PREV + TRF-RIT-1 + TRF-RIT-2 + TRF-RIT-3 + TRF-RIT-4
|
||||
line.append('00'*8) # TRF-UNITA-RICAVI
|
||||
line.append('00'*80) # TRF-UNITA-PAGAM
|
||||
line.append(' '*24) # TRF-FAX-PREF-1 + TRF-FAX-NUM-1
|
||||
line.append(' ' + ' ') # TRF-SOLO-CLIFOR + TRF-80-SEGUENTE
|
||||
line.append('0000000') # TRF-CONTO-RIT-ACC
|
||||
line.append('0'*35) # TRF-CONTO-RIT-PREV + TRF-CONTO-RIT-1 + TRF-CONTO-RIT-2 + TRF-CONTO-RIT-3 + TRF-CONTO-RIT-4
|
||||
line.append('N' + 'N' + '00000000' + '000') # TRF-DIFFERIMENTO-IVA + TRF-STORICO + TRF-STORICO-DATA + TRF-CAUS-ORI
|
||||
line.append(' ' + ' ' + '0'*16 + ' ') # TRF-PREV-TIPOMOV + TRF-PREV-RATRIS + TRF-PREV-DTCOMP-INI + TRF-PREV-DTCOMP-FIN + TRF-PREV-FLAG-CONT
|
||||
line.append(' '*20 + '0'*21 + ' '*44 + '0'*8 + ' ' + '0'*6 + ' ' + '00' + ' ') # TRF-RIFERIMENTO + TRF-CAUS-PREST-ANA + TRF-EC-TIPO-PAGA + TRF-CONTO-IVA-VEN-ACQ + TRF-PIVA-VECCHIA + TRF-PIVA-ESTERO-VECCHIA + # TRF-RISERVATO + TRF-DATA-IVA-AGVIAGGI + TRF-DATI-AGG-ANA-REC4 + TRF-RIF-IVA-NOTE-CRED + TRF-RIF-IVA-ANNO-PREC + TRF-NATURA-GIURIDICA + TRF-STAMPA-ELENCO
|
||||
line.append('000'*8 + ' '*20 + '0' + ' '*4 + '0'*6 + ' '*20 + 'S' + 'N') # TRF-PERC-FORF + TRF-SOLO-MOV-IVA + TRF-COFI-VECCHIO + TRF-USA-PIVA-VECCHIA + TRF-USA-PIVA-EST-VECCHIA + TRF-USA-COFI-VECCHIO + TRF-ESIGIBILITA-IVA + TRF-TIPO-MOV-RISCONTI + TRF-AGGIORNA-EC + TRF-BLACKLIST-ANAG + TRF-BLACKLIST-IVA-ANNO + TRF-CONTEA-ESTERO + TRF-ART21-ANAG + TRF-ART21-IVA
|
||||
|
||||
if invoice["tipoFattura"] == "Fattura":
|
||||
line.append('N') # TRF-RIF-FATTURA
|
||||
else:
|
||||
line.append('S') # TRF-RIF-FATTURA
|
||||
line.append('S' + ' '*2 + 'S' + ' '*2) # TRF-RISERVATO-B + TRF-MASTRO-CF + TRF-MOV-PRIVATO + TRF-SPESE-MEDICHE + TRF-FILLER
|
||||
line.append('\n')
|
||||
parent.log_dialog.log_text.AppendText("Creato record #0 per fattura n. %s\n" % invoice["numFattura"])
|
||||
wx.Yield()
|
||||
|
||||
#RECORD 5 per Tessera Sanitaria
|
||||
line.append('04103' + '3' + '5') # TRF5-DITTA + TRF5-VERSIONE + TRF5-TARC
|
||||
line.append(' '*1200) # TRF-ART21-CONTRATTO
|
||||
line.append('0'*6 + invoice["cf"]) # TRF-A21CO-ANAG + # TRF-A21CO-COFI
|
||||
total = str(invoice["importoTotale"])
|
||||
total = '0'*(13-len(total)) + total + "+"
|
||||
line.append(invoice["dataFattura"] + 'S' + '000' + total + '0'*14 + '0' + invoice["numFattura"][4:9] + '00' + ' '*40) # TRF-A21CO-DATA + TRF-A21CO-FLAG + TRF-A21CO-ALQ + TRF-A21CO-IMPORTO + TRF-A21CO-IMPOSTA + TRF-A21CO-NDOC + TRF-A21CO-CONTRATTO
|
||||
line.append(('0'*6 + ' '*16 + '0'*8 + ' ' + '000' + '0'*14 + '0'*14 + '0'*8 + ' '*40)*49) # TRF-A21CO-DATA + TRF-A21CO-FLAG + TRF-A21CO-ALQ + TRF-A21CO-IMPORTO + TRF-A21CO-IMPOSTA + TRF-A21CO-NDOC + TRF-A21CO-CONTRATTO
|
||||
|
||||
if invoice["tipoFattura"] == "Nota di credito":
|
||||
line.append('000' + invoice["rifFattura"][4:9]) # TRF-RIF-FATT-NDOC
|
||||
line.append('0'*8) # TRF-RIF-FATT-DDOC
|
||||
else:
|
||||
line.append('0'*16) # TRF-RIF-FATT-NDOC + TRF-RIF-FATT-DDOC
|
||||
line.append('F' + 'SR' + '2') # TRF-A21CO-TIPO + TRF-A21CO-TIPO-SPESA + TRF-A21CO-FLAG-SPESA
|
||||
line.append((' ' + ' ' + ' ')*49) # TRF-A21CO-TIPO + TRF-A21CO-TIPO-SPESA + TRF-A21CO-FLAG-SPESA
|
||||
line.append(' ' + 'S' + ' '*76) # TRF-SPESE-FUNEBRI + TRF-A21CO-PAGAM + FILLER + FILLER
|
||||
line.append('\n')
|
||||
parent.log_dialog.log_text.AppendText("Creato record #5 per fattura n. %s\n" % invoice["numFattura"])
|
||||
wx.Yield()
|
||||
|
||||
#RECORD 1 per num. doc. originale
|
||||
line.append('04103' + '3' + '1') # TRF1-DITTA + TRF1-VERSIONE + TRF1-TARC
|
||||
line.append('0'*7 + ' '*3 + '0'*14) # TRF-NUM-AUTOFATT + TRF-SERIE-AUTOFATT + TRF-COD-VAL + TRF-TOTVAL
|
||||
line.append((' '*8 + '0'*24 + ' ' + '0'*36 + ' '*2 + '0'*9 + ' '*5)*20) # TRF-NOMENCLATURA + TRF-IMP-LIRE + TRF-IMP-VAL + TRF-NATURA + TRF-MASSA + TRF-UN-SUPPL + TRF-VAL-STAT + TRF-REGIME + TRF-TRASPORTO + TRF-PAESE-PROV + TRF-PAESE-ORIG + TRF-PAESE-DEST + TRF-PROV-DEST + TRF-PROV-ORIG + TRF-SEGNO-RET
|
||||
line.append(' ' + '0'*6 + ' '*173) # TRF-INTRA-TIPO + TRF-MESE-ANNO-RIF + SPAZIO
|
||||
line.append('0'*45 + ' '*4 + '0'*20 + ' '*28 + '0'*25) # TRF-RITA-TIPO + TRF-RITA-IMPON + TRF-RITA-ALIQ + TRF-RITA-IMPRA + TRF-RITA-PRONS + TRF-RITA-MESE + TRF-RITA-CAUSA + TRF-RITA-TRIBU + TRF-RITA-DTVERS + TRF-RITA-IMPAG + TRF-RITA-TPAG + TRF-RITA-SERIE + TRF-RITA-QUIETANZA + TRF-RITA-NUM-BOLL + TRF-RITA-ABI + TRF-RITA-CAB + TRF-RITA-AACOMP + TRF-RITA-CRED
|
||||
line.append(' ' + '0'*44 + ' '*11 + '0'*64) #TRF-RITA-SOGG + TRF-RITA-BASEIMP + TRF-RITA-FRANCHIGIA + TRF-RITA-CTO-PERC + TRF-RITA-CTO-DITT + FILLER + TRF-RITA-DATA + TRF-RITA-TOTDOC + TRF-RITA-IMPVERS + TRF-RITA-DATA-I + TRF-RITA-DATA-F + TRF-EMENS-ATT + TRF-EMENS-RAP + TRF-EMENS-ASS + TRF-RITA-TOTIVA
|
||||
line.append('0'*6 + ' '*178) # TRF-CAUS-PREST-ANA-B + TRF-RITA-CAUSA-B + FILLER
|
||||
line.append('0'*13 + ' '*30 + '0'*14) # TRF-POR-CODPAG + TRF-POR-BANCA + TRF-POR-AGENZIA + TRF-POR-DESAGENZIA + TRF-POR-TOT-RATE + TRF-POR-TOTDOC
|
||||
line.append(('0'*65 + ' '*2)*12) # TRF-POR-NUM-RATA + TRF-POR-DATASCAD + TRF-POR-TIPOEFF + TRF-POR-IMPORTO-EFF + TRF-POR-IMPORTO-EFFVAL + TRF-POR-IMPORTO-BOLLI + TRF-POR-IMPORTO-BOLLIVAL + TRF-POR-FLAG + TRF-POR-TIPO-RD
|
||||
line.append('0'*4 + ' '*336) # TRF-POR-CODAGE + TRF-POR-EFFETTO-SOSP + TRF-POR-CIG + TRF-POR-CUP + SPAZIO
|
||||
line.append((' '*3 + '0'*16)*20) # TRF-COD-VAL-IV + TRF-IMP-VALUTA-IV
|
||||
line.append((' '*6 + '0'*35 + ' '*2 + '0'*20 + ' '*19 + '0'*16)*20) # TRF-CODICE-SERVIZIO + TRF-STATO-PAGAMENTO + TRF-SERV-IMP-EURO + TRF-SERV-IMP-VAL + TRF-DATA-DOC-ORIG + TRF-MOD-EROGAZIONE + TRF-MOD-INCASSO + TRF-PROT-REG + TRF-PROG-REG + TRF-COD-SEZ-DOG-RET + TRF-ANNO-REG-RET + TRF-NUM-DOC-ORIG + TRF-SERV-SEGNO-RET + TRF-SERV-COD-VAL-IV + TRF-SERV-IMP-VALUTA-IV
|
||||
line.append(' '*1 + '0'*6) # TRF-INTRA-TIPO-SERVIZIO + TRF-SERV-MESE-ANNO-RIF
|
||||
line.append(' '*8) # TRF-CK-RCHARGE
|
||||
line.append('0'*(15-len(invoice["numFattura"])) + invoice["numFattura"]) # TRF-XNUM-DOC-ORI
|
||||
line.append(' ' + '00' + ' '*1090) # TRF-MEM-ESIGIB-IVA + TRF-COD-IDENTIFICATIVO + TRF-ID-IMPORTAZIONE + TRF-XNUM-DOC-ORI-20 + SPAZIO + FILLER
|
||||
parent.log_dialog.log_text.AppendText("Creato record #1 per fattura n. %s\n" % invoice["numFattura"])
|
||||
wx.Yield()
|
||||
|
||||
line = ''.join(line) + '\n'
|
||||
|
||||
traf2000_file.write(line)
|
||||
parent.log_dialog.log_text.AppendText("Convertita fattura n. %s\n" % invoice["numFattura"])
|
||||
wx.Yield()
|
||||
parent.log_dialog.log_text.AppendText("Conversione terminata.\nTracciato TRAF2000 salvato in %s\n" % output_file_path)
|
||||
wx.Yield()
|
Binary file not shown.
6
requirements.txt
Normal file
6
requirements.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
requests_ntlm==1.1.0
|
||||
wxPython==4.0.7
|
||||
requests==2.24.0
|
||||
openpyxl==3.0.5
|
||||
Unidecode==1.1.2
|
||||
PyPDF2==1.26.0
|
32
setup.py
Normal file
32
setup.py
Normal file
@@ -0,0 +1,32 @@
|
||||
import setuptools
|
||||
|
||||
with open('README.md', 'r', encoding='utf-8') as readme:
|
||||
long_description = readme.read()
|
||||
|
||||
setuptools.setup(
|
||||
name='fatture_ccsr',
|
||||
version='0.0.1',
|
||||
author='Ettore Dreucci',
|
||||
author_email='ettore.dreucci@gmail.com',
|
||||
description='Utility to download or convert CCSR invoices to TeamSystem\'s TRAF2000 record',
|
||||
long_description=long_description,
|
||||
long_description_content_type='text/markdown',
|
||||
url='https://github.com/Noettore/fattureSanRossore',
|
||||
packages=setuptools.find_packages(),
|
||||
install_requires=[
|
||||
'wxPython',
|
||||
'requests',
|
||||
'Unidecode',
|
||||
'requests_ntlm',
|
||||
'openpyxl',
|
||||
'PyPDF2',
|
||||
],
|
||||
include_package_data=True,
|
||||
license='MIT',
|
||||
classifiers=[
|
||||
"Programming Language :: Python :: 3",
|
||||
"License :: OSI Approved :: MIT License",
|
||||
"Operating System :: OS Independent",
|
||||
],
|
||||
python_requires='>=3.7',
|
||||
)
|
@@ -1,29 +0,0 @@
|
||||
# fattureSanRossore Downloader
|
||||
|
||||
[](../LICENSE.md) [](#) [](https://github.com/Noettore/fattureSanRossore/commit/master)
|
||||
|
||||
## Getting Started
|
||||
|
||||
These instructions will get you a copy of the project up and running on your local machine for development and testing purposes.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [Python3](https://www.python.org/)
|
||||
|
||||
### Run
|
||||
|
||||
To run you can simply execute python script.
|
||||
It will ask for an input(.csv) and an output(TRAF2000) file.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- [unidecode](https://pypi.org/project/Unidecode/)
|
||||
- [easygui](https://pypi.org/project/easygui/)
|
||||
|
||||
## Author
|
||||
|
||||
- [**Ettore Dreucci**](https://ettore.dreucci.it)
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the MIT License - see the [LICENSE.md](../LICENSE.md) file for details
|
@@ -1,89 +0,0 @@
|
||||
"""This script provides two functions to import invoices from .csv or .xml into a dict"""
|
||||
|
||||
import csv
|
||||
import xml.etree.ElementTree
|
||||
import datetime
|
||||
import unidecode
|
||||
|
||||
def import_csv(csv_file_path):
|
||||
"""Return a dict containing the invoices info"""
|
||||
fatture = dict()
|
||||
with open(csv_file_path, newline="") as csv_file:
|
||||
lettore = csv.reader(csv_file, delimiter=",")
|
||||
|
||||
for _ in range(4):
|
||||
next(lettore)
|
||||
|
||||
for linea in lettore:
|
||||
if len(linea) == 0:
|
||||
break
|
||||
num_fattura = linea[1]
|
||||
tipo_fattura = linea[8]
|
||||
importo = linea[15]
|
||||
segno = 1
|
||||
if tipo_fattura == "Nota di credito" and '(' not in importo:
|
||||
segno = -1
|
||||
importo = int(linea[15].replace("€", "").replace(",", "").replace(".", "").replace("(", "").replace(")", "")) * segno
|
||||
if num_fattura not in fatture:
|
||||
fattura = {
|
||||
"numFattura": num_fattura,
|
||||
"tipoFattura": tipo_fattura,
|
||||
"rifFattura": linea[4],
|
||||
"dataFattura": linea[2].replace("/", ""),
|
||||
"ragioneSociale": unidecode.unidecode(linea[6] + " " + " ".join(linea[5].split(" ")[0:2])),
|
||||
"posDivide": str(len(linea[6]) + 1),
|
||||
"cf": linea[7],
|
||||
"importoTotale": 0,
|
||||
"ritenutaAcconto": 0,
|
||||
"righe": dict()
|
||||
}
|
||||
fatture[num_fattura] = fattura
|
||||
|
||||
if linea[14] == "Ritenuta d'acconto":
|
||||
fatture[num_fattura]["ritenutaAcconto"] = importo
|
||||
|
||||
else:
|
||||
fatture[num_fattura]["importoTotale"] += importo
|
||||
fatture[num_fattura]["righe"][linea[14]] = importo
|
||||
return fatture
|
||||
|
||||
def import_xml(xml_file_path):
|
||||
"""Return a dict containing the invoices info"""
|
||||
fatture = dict()
|
||||
|
||||
tree = xml.etree.ElementTree.parse(xml_file_path)
|
||||
root = tree.getroot()
|
||||
|
||||
for fattura in root.iter('{STAT_FATTURATO_CTERZI}Dettagli'):
|
||||
righe = dict()
|
||||
num_fattura = fattura.get('protocollo_fatturatestata')
|
||||
tipo_fattura = fattura.get('fat_ndc')
|
||||
importo_totale = 0
|
||||
ritenuta_acconto = 0
|
||||
|
||||
for riga in fattura.iter('{STAT_FATTURATO_CTERZI}Dettagli2'):
|
||||
desc = riga.get('descrizione_fatturariga1')
|
||||
segno = 1
|
||||
if tipo_fattura == 'Nota di credito' and '-' not in riga.get('prezzounitario_fatturariga1'):
|
||||
segno = -1
|
||||
importo = int(format(round(float(riga.get('prezzounitario_fatturariga1')), 2), '.2f').replace('.', '').replace('-', '')) * segno
|
||||
if desc == "Ritenuta d'acconto":
|
||||
ritenuta_acconto = importo
|
||||
else:
|
||||
righe[desc] = importo
|
||||
importo_totale += importo
|
||||
|
||||
fattura_elem = {
|
||||
"numFattura": num_fattura,
|
||||
"tipoFattura": tipo_fattura,
|
||||
"rifFattura": fattura.get('protocollo_fatturatestata1'),
|
||||
"dataFattura": datetime.datetime.fromisoformat(fattura.get('data_fatturatestata')).strftime("%d%m%Y"),
|
||||
"ragioneSociale": unidecode.unidecode(fattura.get('cognome_cliente') + ' ' + ' '.join(fattura.get('nome_cliente').split(' ')[0:2])),
|
||||
"posDivide": str(len(fattura.get('cognome_cliente')) + 1),
|
||||
"cf": fattura.get('cf_piva_cliente'),
|
||||
"importoTotale": importo_totale,
|
||||
"ritenutaAcconto": ritenuta_acconto,
|
||||
"righe": righe,
|
||||
}
|
||||
fatture[num_fattura] = fattura_elem
|
||||
return fatture
|
@@ -1,53 +0,0 @@
|
||||
"""This script ask for an input file and an output file and generates the TRAF2000 records from a .csv or .xml"""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import wx
|
||||
|
||||
import fatture_import
|
||||
import traf2000_convert
|
||||
|
||||
def get_input_file(wildcard):
|
||||
"""Return the input file path"""
|
||||
_ = wx.App(None)
|
||||
style = wx.FD_OPEN | wx.FD_FILE_MUST_EXIST
|
||||
dialog = wx.FileDialog(None, "Scegli il .csv o .xml contenente le informazioni sulle fatture da importare", wildcard=wildcard, style=style)
|
||||
if dialog.ShowModal() == wx.ID_OK:
|
||||
path = dialog.GetPath()
|
||||
else:
|
||||
path = None
|
||||
dialog.Destroy()
|
||||
return path
|
||||
|
||||
def get_output_file(default_output_filename):
|
||||
"""Return the output file path"""
|
||||
_ = wx.App(None)
|
||||
style = wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT
|
||||
dialog = wx.FileDialog(None, "Scegli dove salvare il file TRAF2000", defaultFile=default_output_filename, style=style)
|
||||
if dialog.ShowModal() == wx.ID_OK:
|
||||
path = dialog.GetPath()
|
||||
else:
|
||||
path = None
|
||||
dialog.Destroy()
|
||||
return path
|
||||
|
||||
input_file_path = get_input_file("*.csv;*.xml")
|
||||
if input_file_path is None:
|
||||
sys.exit("ERROR: No input file selected!")
|
||||
|
||||
fattureFileExtension = os.path.splitext(input_file_path)[1]
|
||||
|
||||
output_file_path = get_output_file("TRAF2000")
|
||||
if output_file_path is None:
|
||||
sys.exit("ERROR: No output file selected!")
|
||||
|
||||
if fattureFileExtension == ".csv":
|
||||
fatture = fatture_import.import_csv(input_file_path)
|
||||
|
||||
elif fattureFileExtension == ".xml":
|
||||
fatture = fatture_import.import_xml(input_file_path)
|
||||
|
||||
else:
|
||||
sys.exit("ERROR: file extension not supported")
|
||||
|
||||
traf2000_convert.convert(fatture, output_file_path)
|
@@ -1,177 +0,0 @@
|
||||
"""This script provides a function to convert a dict of invoices to a TRAF2000 file"""
|
||||
|
||||
def convert(fatture, out_file_path):
|
||||
"""Output to a file the TRAF2000 records"""
|
||||
with open(out_file_path, "w") as traf2000_file:
|
||||
print("Note di credito:\n")
|
||||
|
||||
for fattura in fatture.values():
|
||||
if fattura["tipoFattura"] != "Fattura" and fattura["tipoFattura"] != "Nota di credito":
|
||||
print("Errore: il documento " + fattura["numFattura"] + " può essere FATTURA o NOTA DI CREDITO")
|
||||
continue
|
||||
|
||||
if len(fattura["cf"]) != 16 and len(fattura["cf"]) == 11:
|
||||
print("Errore: il documento " + fattura["numFattura"] + " non ha cf/piva")
|
||||
continue
|
||||
|
||||
if fattura["tipoFattura"] == "Nota di credito":
|
||||
# As for now this script doesn't handle "Note di credito"
|
||||
print(fattura["numFattura"])
|
||||
continue
|
||||
|
||||
linea = ["04103", "3", "0", "00000"] # TRF-DITTA + TRF-VERSIONE + TRF-TARC + TRF-COD-CLIFOR
|
||||
linea.append(fattura["ragioneSociale"][:32]+' '*(32-len(fattura["ragioneSociale"]))) # TRF-RASO
|
||||
linea.append(' '*30) # TRF-IND
|
||||
linea.append('00000') # TRF-CAP
|
||||
linea.append(' '*27) # TRF-CITTA + TRF-PROV
|
||||
|
||||
if len(fattura["cf"]) == 16: # se c.f. presente
|
||||
linea.append(fattura["cf"]) # TRF-COFI
|
||||
linea.append('0'*11) # TRF-PIVA
|
||||
linea.append('S') # TRF-PF
|
||||
else: # se piva presente
|
||||
linea.append(' '*16) # TRF-COFI
|
||||
linea.append(fattura["cf"]) # TRF-PIVA
|
||||
linea.append('N') # TRF-PF
|
||||
|
||||
linea.append('0'*(2-len(fattura["posDivide"])) + fattura["posDivide"]) # TRF-DIVIDE
|
||||
linea.append('0000') # TRF-PAESE
|
||||
linea.append(' '*33) # TRF-PIVA-ESTERO + TRF-COFI-ESTERO + TRF-SESSO
|
||||
linea.append('0'*8) # TRF-DTNAS
|
||||
linea.append(' '*64) # TRF-COMNA + TRF-PRVNA + TRF-PREF + TRF-NTELE-NUM + TRF-FAX-PREF + TRF-FAX-NUM
|
||||
linea.append('0'*22) # TRF-CFCONTO + TRF-CFCODPAG + TRF-CFBANCA + TRF-CFAGENZIA + TRF-CFINTERM
|
||||
|
||||
if fattura["tipoFattura"] == "Fattura":
|
||||
linea.append('001') # TRF-CAUSALE
|
||||
linea.append("FATTURA VENDITA") # TRF-CAU-DES
|
||||
else:
|
||||
linea.append('002') # TRF-CAUSALE
|
||||
linea.append("N.C. A CLIENTE ") # TRF-CAU-DES
|
||||
|
||||
linea.append(' '*86) # TRF-CAU-AGG + TRF-CAU-AGG-1 + TRF-CAU-AGG-2
|
||||
linea.append(fattura["dataFattura"]*2) # TRF-DATA-REGISTRAZIONE + TRF-DATA-DOC
|
||||
linea.append('00000000') # TRF-NUM-DOC-FOR
|
||||
linea.append(fattura["numFattura"][4:9]) # TRF-NDOC
|
||||
linea.append('00') # TRF-SERIE
|
||||
linea.append('0'*72) # TRF-EC-PARTITA + TRF-EC-PARTITA-ANNO + TRF-EC-COD-VAL + TRF-EC-CAMBIO + TRF-EC-DATA-CAMBIO + TRF-EC-TOT-DOC-VAL + TRF-EC-TOT-IVA-VAL + TRF-PLAFOND
|
||||
|
||||
conta = 0
|
||||
for desc, imponibile in fattura["righe"].items():
|
||||
conta += 1
|
||||
imponibile = str(imponibile)
|
||||
if '-' in imponibile:
|
||||
imponibile.replace('-', '')
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "-"
|
||||
else:
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "+"
|
||||
linea.append(imponibile) # TRF-IMPONIB
|
||||
if desc != "Bollo":
|
||||
linea.append('308') # TRF-ALIQ
|
||||
else:
|
||||
linea.append('315') # TRF-ALIQ
|
||||
linea.append('0'*16) # TRF-ALIQ-AGRICOLA + TRF-IVA11 + TRF-IMPOSTA
|
||||
|
||||
for _ in range(8-conta):
|
||||
linea.append('0'*31)
|
||||
|
||||
totale = str(fattura["importoTotale"])
|
||||
if '-' in totale:
|
||||
totale.replace('-', '')
|
||||
totale = '0'*(11-len(totale)) + totale + "-"
|
||||
else:
|
||||
totale = '0'*(11-len(totale)) + totale + "+"
|
||||
linea.append(totale) # TRF-TOT-FAT
|
||||
|
||||
conta = 0
|
||||
for desc, imponibile in fattura["righe"].items():
|
||||
conta += 1
|
||||
imponibile = str(imponibile)
|
||||
if '-' in imponibile:
|
||||
imponibile.replace('-', '')
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "-"
|
||||
else:
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "+"
|
||||
if desc != "Bollo":
|
||||
linea.append('4004300') # TRF-CONTO-RIC
|
||||
else:
|
||||
linea.append('4004500') # TRF-CONTO-RIC
|
||||
linea.append(imponibile) # TRF-IMP-RIC
|
||||
|
||||
for _ in range(8-conta):
|
||||
linea.append('0'*19)
|
||||
|
||||
linea.append('000') # TRF-CAU-PAG
|
||||
linea.append(' '*83) # TRF-CAU-DES-PAGAM + TRF-CAU-AGG-1-PAGAM + TRF-CAU-AGG-2-PAGAM
|
||||
linea.append(('0000000' + ' ' + '0'*12 + ' '*18 + '0'*26)*80) # TRF-CONTO + TRF-DA + TRF-IMPORTO + TRF-CAU-AGGIUNT + TRF-EC-PARTITA-PAG + TRF-EC-PARTITA-ANNO-PAG + TRF-EC-IMP-VAL
|
||||
linea.append((' ' + '0'*18)*10) # TRF-RIFER-TAB + TRF-IND-RIGA + TRF-DT-INI + TRF-DT-FIN
|
||||
linea.append('000000') # TRF-DOC6
|
||||
linea.append('N' + '0') # TRF-AN-OMONIMI + TRF-AN-TIPO-SOGG
|
||||
linea.append('00'*80) # TRF-EC-PARTITA-SEZ-PAG
|
||||
linea.append('0'*15) # TRF-NUM-DOC-PAG-PROF + TRF-DATA-DOC-PAG-PROF
|
||||
|
||||
if fattura["ritenutaAcconto"] != 0:
|
||||
imponibile = str(fattura["ritenutaAcconto"])
|
||||
imponibile = '0'*(11-len(imponibile)) + imponibile + "+"
|
||||
linea.append(imponibile) # TRF-RIT-ACC
|
||||
else:
|
||||
linea.append('0'*12) # TRF-RIT-ACC
|
||||
|
||||
linea.append('0'*60) # TRF-RIT-PREV + TRF-RIT-1 + TRF-RIT-2 + TRF-RIT-3 + TRF-RIT-4
|
||||
linea.append('00'*8) # TRF-UNITA-RICAVI
|
||||
linea.append('00'*80) # TRF-UNITA-PAGAM
|
||||
linea.append(' '*24) # TRF-FAX-PREF-1 + TRF-FAX-NUM-1
|
||||
linea.append(' ' + ' ') # TRF-SOLO-CLIFOR + TRF-80-SEGUENTE
|
||||
linea.append('0000000') # TRF-CONTO-RIT-ACC
|
||||
linea.append('0'*35) # TRF-CONTO-RIT-PREV + TRF-CONTO-RIT-1 + TRF-CONTO-RIT-2 + TRF-CONTO-RIT-3 + TRF-CONTO-RIT-4
|
||||
linea.append('N' + 'N' + '00000000' + '000') # TRF-DIFFERIMENTO-IVA + TRF-STORICO + TRF-STORICO-DATA + TRF-CAUS-ORI
|
||||
linea.append(' ' + ' ' + '0'*16 + ' ') # TRF-PREV-TIPOMOV + TRF-PREV-RATRIS + TRF-PREV-DTCOMP-INI + TRF-PREV-DTCOMP-FIN + TRF-PREV-FLAG-CONT
|
||||
linea.append(' '*20 + '0'*21 + ' '*44 + '0'*8 + ' ' + '0'*6 + ' ' + '00' + ' ') # TRF-RIFERIMENTO + TRF-CAUS-PREST-ANA + TRF-EC-TIPO-PAGA + TRF-CONTO-IVA-VEN-ACQ + TRF-PIVA-VECCHIA + TRF-PIVA-ESTERO-VECCHIA + # TRF-RISERVATO + TRF-DATA-IVA-AGVIAGGI + TRF-DATI-AGG-ANA-REC4 + TRF-RIF-IVA-NOTE-CRED + TRF-RIF-IVA-ANNO-PREC + TRF-NATURA-GIURIDICA + TRF-STAMPA-ELENCO
|
||||
linea.append('000'*8 + ' '*20 + '0' + ' '*4 + '0'*6 + ' '*20 + 'S' + 'N') # TRF-PERC-FORF + TRF-SOLO-MOV-IVA + TRF-COFI-VECCHIO + TRF-USA-PIVA-VECCHIA + TRF-USA-PIVA-EST-VECCHIA + TRF-USA-COFI-VECCHIO + TRF-ESIGIBILITA-IVA + TRF-TIPO-MOV-RISCONTI + TRF-AGGIORNA-EC + TRF-BLACKLIST-ANAG + TRF-BLACKLIST-IVA-ANNO + TRF-CONTEA-ESTERO + TRF-ART21-ANAG + TRF-ART21-IVA
|
||||
|
||||
if fattura["tipoFattura"] == "Fattura":
|
||||
linea.append('N') # TRF-RIF-FATTURA
|
||||
else:
|
||||
linea.append('S') # TRF-RIF-FATTURA
|
||||
linea.append('S' + ' '*2 + 'S' + ' '*2) # TRF-RISERVATO-B + TRF-MASTRO-CF + TRF-MOV-PRIVATO + TRF-SPESE-MEDICHE + TRF-FILLER
|
||||
linea.append('\n')
|
||||
|
||||
#RECORD 5 per Tessera Sanitaria
|
||||
linea.append('04103' + '3' + '5') # TRF5-DITTA + TRF5-VERSIONE + TRF5-TARC
|
||||
linea.append(' '*1200) # TRF-ART21-CONTRATTO
|
||||
linea.append('0'*6 + fattura["cf"]) # TRF-A21CO-ANAG + # TRF-A21CO-COFI
|
||||
totale = str(fattura["importoTotale"])
|
||||
totale = '0'*(13-len(totale)) + totale + "+"
|
||||
linea.append(fattura["dataFattura"] + 'S' + '000' + totale + '0'*14 + '0' + fattura["numFattura"][4:9] + '00' + ' '*40) # TRF-A21CO-DATA + TRF-A21CO-FLAG + TRF-A21CO-ALQ + TRF-A21CO-IMPORTO + TRF-A21CO-IMPOSTA + TRF-A21CO-NDOC + TRF-A21CO-CONTRATTO
|
||||
linea.append(('0'*6 + ' '*16 + '0'*8 + ' ' + '000' + '0'*14 + '0'*14 + '0'*8 + ' '*40)*49) # TRF-A21CO-DATA + TRF-A21CO-FLAG + TRF-A21CO-ALQ + TRF-A21CO-IMPORTO + TRF-A21CO-IMPOSTA + TRF-A21CO-NDOC + TRF-A21CO-CONTRATTO
|
||||
|
||||
if fattura["tipoFattura"] == "Nota di credito":
|
||||
linea.append('000' + fattura["rifFattura"][4:9]) # TRF-RIF-FATT-NDOC
|
||||
linea.append('0'*8) # TRF-RIF-FATT-DDOC
|
||||
else:
|
||||
linea.append('0'*16) # TRF-RIF-FATT-NDOC + TRF-RIF-FATT-DDOC
|
||||
linea.append('F' + 'SR' + '2') # TRF-A21CO-TIPO + TRF-A21CO-TIPO-SPESA + TRF-A21CO-FLAG-SPESA
|
||||
linea.append((' ' + ' ' + ' ')*49) # TRF-A21CO-TIPO + TRF-A21CO-TIPO-SPESA + TRF-A21CO-FLAG-SPESA
|
||||
linea.append(' ' + 'S' + ' '*76) # TRF-SPESE-FUNEBRI + TRF-A21CO-PAGAM + FILLER + FILLER
|
||||
linea.append('\n')
|
||||
|
||||
#RECORD 1 per num. doc. originale
|
||||
linea.append('04103' + '3' + '1') # TRF1-DITTA + TRF1-VERSIONE + TRF1-TARC
|
||||
linea.append('0'*7 + ' '*3 + '0'*14) # TRF-NUM-AUTOFATT + TRF-SERIE-AUTOFATT + TRF-COD-VAL + TRF-TOTVAL
|
||||
linea.append((' '*8 + '0'*24 + ' ' + '0'*36 + ' '*2 + '0'*9 + ' '*5)*20) # TRF-NOMENCLATURA + TRF-IMP-LIRE + TRF-IMP-VAL + TRF-NATURA + TRF-MASSA + TRF-UN-SUPPL + TRF-VAL-STAT + TRF-REGIME + TRF-TRASPORTO + TRF-PAESE-PROV + TRF-PAESE-ORIG + TRF-PAESE-DEST + TRF-PROV-DEST + TRF-PROV-ORIG + TRF-SEGNO-RET
|
||||
linea.append(' ' + '0'*6 + ' '*173) # TRF-INTRA-TIPO + TRF-MESE-ANNO-RIF + SPAZIO
|
||||
linea.append('0'*45 + ' '*4 + '0'*20 + ' '*28 + '0'*25) # TRF-RITA-TIPO + TRF-RITA-IMPON + TRF-RITA-ALIQ + TRF-RITA-IMPRA + TRF-RITA-PRONS + TRF-RITA-MESE + TRF-RITA-CAUSA + TRF-RITA-TRIBU + TRF-RITA-DTVERS + TRF-RITA-IMPAG + TRF-RITA-TPAG + TRF-RITA-SERIE + TRF-RITA-QUIETANZA + TRF-RITA-NUM-BOLL + TRF-RITA-ABI + TRF-RITA-CAB + TRF-RITA-AACOMP + TRF-RITA-CRED
|
||||
linea.append(' ' + '0'*44 + ' '*11 + '0'*64) #TRF-RITA-SOGG + TRF-RITA-BASEIMP + TRF-RITA-FRANCHIGIA + TRF-RITA-CTO-PERC + TRF-RITA-CTO-DITT + FILLER + TRF-RITA-DATA + TRF-RITA-TOTDOC + TRF-RITA-IMPVERS + TRF-RITA-DATA-I + TRF-RITA-DATA-F + TRF-EMENS-ATT + TRF-EMENS-RAP + TRF-EMENS-ASS + TRF-RITA-TOTIVA
|
||||
linea.append('0'*6 + ' '*178) # TRF-CAUS-PREST-ANA-B + TRF-RITA-CAUSA-B + FILLER
|
||||
linea.append('0'*13 + ' '*30 + '0'*14) # TRF-POR-CODPAG + TRF-POR-BANCA + TRF-POR-AGENZIA + TRF-POR-DESAGENZIA + TRF-POR-TOT-RATE + TRF-POR-TOTDOC
|
||||
linea.append(('0'*65 + ' '*2)*12) # TRF-POR-NUM-RATA + TRF-POR-DATASCAD + TRF-POR-TIPOEFF + TRF-POR-IMPORTO-EFF + TRF-POR-IMPORTO-EFFVAL + TRF-POR-IMPORTO-BOLLI + TRF-POR-IMPORTO-BOLLIVAL + TRF-POR-FLAG + TRF-POR-TIPO-RD
|
||||
linea.append('0'*4 + ' '*336) # TRF-POR-CODAGE + TRF-POR-EFFETTO-SOSP + TRF-POR-CIG + TRF-POR-CUP + SPAZIO
|
||||
linea.append((' '*3 + '0'*16)*20) # TRF-COD-VAL-IV + TRF-IMP-VALUTA-IV
|
||||
linea.append((' '*6 + '0'*35 + ' '*2 + '0'*20 + ' '*19 + '0'*16)*20) # TRF-CODICE-SERVIZIO + TRF-STATO-PAGAMENTO + TRF-SERV-IMP-EURO + TRF-SERV-IMP-VAL + TRF-DATA-DOC-ORIG + TRF-MOD-EROGAZIONE + TRF-MOD-INCASSO + TRF-PROT-REG + TRF-PROG-REG + TRF-COD-SEZ-DOG-RET + TRF-ANNO-REG-RET + TRF-NUM-DOC-ORIG + TRF-SERV-SEGNO-RET + TRF-SERV-COD-VAL-IV + TRF-SERV-IMP-VALUTA-IV
|
||||
linea.append(' '*1 + '0'*6) # TRF-INTRA-TIPO-SERVIZIO + TRF-SERV-MESE-ANNO-RIF
|
||||
linea.append(' '*8) # TRF-CK-RCHARGE
|
||||
linea.append('0'*(15-len(fattura["numFattura"])) + fattura["numFattura"]) # TRF-XNUM-DOC-ORI
|
||||
linea.append(' ' + '00' + ' '*1090) # TRF-MEM-ESIGIB-IVA + TRF-COD-IDENTIFICATIVO + TRF-ID-IMPORTAZIONE + TRF-XNUM-DOC-ORI-20 + SPAZIO + FILLER
|
||||
|
||||
linea = ''.join(linea) + '\n'
|
||||
|
||||
traf2000_file.write(linea)
|
Reference in New Issue
Block a user