Aggiunta gestione export csv

This commit is contained in:
rexeref committed 2026-10-10 20:40:24 +02:00
1 parent 8b5b690435
commit 8cd79da5eb
8 files changed
+376 -42

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
{
"productsFile":".\/pm-item-price-default.json",
"salesFile":".\/pm-item-sold-count-default.json"
}
+4
View File
@@ -0,0 +1,4 @@
Nome;Inventario iniziale;Vendite;Prezzo;Componenti
zainetto;17;0;20.00;
mozzarrella;16;1;3.00;
3x2 mozzarelle;non definito;1;6.00;mozzarrella
1 Nome Inventario iniziale Vendite Prezzo Componenti
2 zainetto 17 0 20.00
3 mozzarrella 16 1 3.00
4 3x2 mozzarelle non definito 1 6.00 mozzarrella
+5 -2
View File
@@ -33,13 +33,16 @@ int consumeProductInventory(const char *fname, const char *name, int qty);
int getCount(const char *fname, const char *name);
void listSold(const char *fname);
int addSold(const char *fname, const char *name, int qty);
int updateSold(const char *fname, const char *name, int qty);
int deleteSold(const char *fname, const char *name);
int deleteSold(const char *productsFile, const char *soldFile, const char *name);
int updateSold(const char *productsFile, const char *soldFile, const char *name, int qty);
int recordSale(const char *productsFile, const char *soldFile, const char *name, int qty);
/* Entrate */
double getTotalRevenue(const char *productsFile, const char *soldFile);
/* Export */
int exportSalesToCsv(const char *productsFile, const char *soldFile, const char *outFile);
/* Config */
int ensureAppConfig(const char *fname);
int loadAppConfig(const char *fname);
+1
View File
@@ -14,6 +14,7 @@ void runAboutWindow();
void runViewSalesWindow();
void runViewProductsWindow();
void runAddProductWindow();
void runExportCsvWindow();
void example_window();
#endif
+54
View File
@@ -0,0 +1,54 @@
[
{
"name":"zainetto",
"price":20.0,
"stock":15
},
{
"name":"mozzarrella",
"price":3.0,
"stock":15
},
{
"name":"3x2 mozzarelle",
"price":6.0,
"bundle":1,
"stock":-1,
"components":[
{
"name":"mozzarrella",
"qty":3
}
]
},
{
"name":"astuccio",
"price":10.0,
"stock":-1
},
{
"name":"set penne matite",
"price":8.0,
"stock":30
},
{
"name":"Scuola",
"price":50.0,
"bundle":1,
"stock":-1,
"components":[
{
"name":"zainetto",
"qty":1
},
{
"name":"astuccio",
"qty":1
},
{
"name":"set penne matite",
"qty":2
}
]
}
]
+14
View File
@@ -0,0 +1,14 @@
[
{
"name":"mozzarrella",
"sold":1
},
{
"name":"3x2 mozzarelle",
"sold":1
},
{
"name":"Scuola",
"sold":2
}
]
+221 -22
View File
@@ -5,6 +5,8 @@
#include <json-c/json.h>
#include "../include/logic.h"
#include <stdio.h>
#include "../include/globals.h"
#include "../include/jsonFile.h"
@@ -39,6 +41,90 @@ static int readInventory(json_object *item, int *stock, int *tracked) {
return 0;
}
int exportSalesToCsv(const char *productsFile, const char *soldFile, const char *outFile)
{
json_object *products = getProducts(productsFile);
json_object *sales = getProducts(soldFile);
if (!products || !json_object_is_type(products, json_type_array)) {
if (products) json_object_put(products);
if (sales) json_object_put(sales);
return -1;
}
FILE *f = fopen(outFile, "w");
if (!f) {
if (products) json_object_put(products);
if (sales) json_object_put(sales);
return -2;
}
fprintf(f, "Nome;Inventario iniziale;Vendite;Prezzo;Componenti\n");
int n = json_object_array_length(products);
for (int i = 0; i < n; ++i) {
json_object *item = json_object_array_get_idx(products, i);
json_object *jname = NULL;
json_object *jprice = NULL;
json_object *jbundle = NULL;
const char *name = (json_object_object_get_ex(item, "name", &jname) && jname) ? json_object_get_string(jname) : "";
double price = (json_object_object_get_ex(item, "price", &jprice) && jprice) ? json_object_get_double(jprice) : 0.0;
int isBundle = (json_object_object_get_ex(item, "bundle", &jbundle) && jbundle) ? json_object_get_int(jbundle) : 0;
/* sold count */
int sold = getCount(soldFile, name);
/* inventory */
const char *inv_text = NULL;
char inv_buf[64];
if (isBundle) {
inv_text = "non definito";
} else {
int stock = -1;
json_object *stock_obj = NULL;
if (json_object_object_get_ex(item, "stock", &stock_obj) && stock_obj) {
stock = json_object_get_int(stock_obj);
}
if (stock < 0) {
inv_text = "non definito";
} else {
/* initial = current stock + sold */
snprintf(inv_buf, sizeof(inv_buf), "%d", stock + sold);
inv_text = inv_buf;
}
}
/* components */
char comp_buf[512];
comp_buf[0] = '\0';
if (isBundle) {
json_object *comp = NULL;
if (json_object_object_get_ex(item, "components", &comp) && comp && json_object_is_type(comp, json_type_array)) {
int m = json_object_array_length(comp);
for (int j = 0; j < m; ++j) {
json_object *c = json_object_array_get_idx(comp, j);
json_object *cjname = NULL;
const char *cname = (json_object_object_get_ex(c, "name", &cjname) && cjname) ? json_object_get_string(cjname) : "";
if (j) strncat(comp_buf, ";", sizeof(comp_buf)-strlen(comp_buf)-1);
strncat(comp_buf, cname ? cname : "", sizeof(comp_buf)-strlen(comp_buf)-1);
}
}
}
/* write CSV line; escape semicolons in name/comp if needed (simple) */
fprintf(f, "%s;%s;%d;%.2f;%s\n",
name ? name : "",
inv_text,
sold,
price,
comp_buf);
}
fclose(f);
if (products) json_object_put(products);
if (sales) json_object_put(sales);
return 0;
}
static void writeInventory(json_object *item, int stock, int tracked) {
if (!item)
return;
@@ -267,6 +353,46 @@ static int consumeTree(json_object *products, json_object *item, int qty) {
return 0;
}
static int restoreTree(json_object *products, json_object *item, int qty) {
if (!products || !item || qty <= 0) return -1;
if (!isBundleItem(item)) {
json_object *jstock = NULL;
if (!json_object_object_get_ex(item, "stock", &jstock) || !jstock)
return 0;
int stock = json_object_get_int(jstock);
if (stock < 0) return 0; /* non tracciato */
json_object_object_add(item, "stock", json_object_new_int(stock + qty));
return 0;
}
json_object *components = getBundleComponents(item);
if (!components) return 0;
size_t count = json_object_array_length(components);
for (size_t i = 0; i < count; i++) {
json_object *c = json_object_array_get_idx(components, i);
json_object *jname = NULL;
if (!json_object_object_get_ex(c, "name", &jname) || !jname) return -1;
json_object *child = findItemByName(products, json_object_get_string(jname));
if (!child) return 1;
int res = restoreTree(products, child, qty * getComponentQty(c));
if (res != 0) return res;
}
return 0;
}
int restoreProductInventory(const char *fname, const char *name, int qty) {
json_object *arr = loadJsonArray(fname);
if (!arr) return -1;
json_object *item = findItemByName(arr, name);
if (!item) { json_object_put(arr); return 1; }
int res = restoreTree(arr, item, qty);
if (res == 0) res = saveJsonArray(fname, arr);
json_object_put(arr);
return res == 0 ? 0 : -1;
}
static char *loadFileText(const char *fname) {
FILE *f = fopen(fname, "rb");
long size;
@@ -692,38 +818,111 @@ int recordSale(const char *productsFile, const char *soldFile, const char *name,
return 0;
}
int updateSold(const char *fname, const char *name, int qty) {
json_object *arr = loadJsonArray(fname);
if (!arr)
int deleteSold(const char *productsFile, const char *soldFile, const char *name) {
json_object *sales = loadJsonArray(soldFile);
if (!sales)
return -1;
json_object *item = findItemByName(arr, name);
json_object *item = findItemByName(sales, name);
if (!item) {
json_object_put(arr);
return 1;
json_object_put(sales);
return 1; /* vendita non presente */
}
json_object *jsold = NULL;
int qty = 0;
if (json_object_object_get_ex(item, "sold", &jsold) && jsold)
qty = json_object_get_int(jsold);
/* backup dei prodotti per il rollback */
char *backup = NULL;
if (qty > 0) {
backup = loadFileText(productsFile);
if (!backup) {
json_object_put(sales);
return -1;
}
int res = restoreProductInventory(productsFile, name, qty);
/* 1 = prodotto non più presente: niente da ripristinare, si procede */
if (res != 0 && res != 1) {
free(backup);
json_object_put(sales);
return res;
}
}
int res = removeItemByName(sales, name);
if (res == 0)
res = saveJsonArray(soldFile, sales);
json_object_put(sales);
if (res != 0) {
if (backup)
saveFileText(productsFile, backup); /* rollback inventario */
free(backup);
return (res < 0) ? res : -1;
}
free(backup);
return 0;
}
int updateSold(const char *productsFile, const char *soldFile, const char *name, int qty) {
if (qty < 0)
return -1;
json_object *sales = loadJsonArray(soldFile);
if (!sales)
return -1;
json_object *item = findItemByName(sales, name);
if (!item) {
json_object_put(sales);
return 1; /* vendita non presente */
}
json_object *jsold = NULL;
int oldQty = 0;
if (json_object_object_get_ex(item, "sold", &jsold) && jsold)
oldQty = json_object_get_int(jsold);
int delta = qty - oldQty; /* >0: vendute di più, <0: vendute di meno */
char *backup = NULL;
if (delta != 0) {
backup = loadFileText(productsFile);
if (!backup) {
json_object_put(sales);
return -1;
}
int res;
if (delta > 0)
res = consumeProductInventory(productsFile, name, delta); /* controlla anche la disponibilità */
else
res = restoreProductInventory(productsFile, name, -delta);
if (res != 0 && !(delta < 0 && res == 1)) {
free(backup);
json_object_put(sales);
return (res == 1) ? -1 : res; /* evita ambiguità con "vendita non presente" */
}
}
json_object_object_add(item, "sold", json_object_new_int(qty));
int res = saveJsonArray(soldFile, sales);
json_object_put(sales);
int res = saveJsonArray(fname, arr);
json_object_put(arr);
return (res == 0) ? 0 : -1;
}
int deleteSold(const char *fname, const char *name) {
json_object *arr = loadJsonArray(fname);
if (!arr)
return -1;
int res = removeItemByName(arr, name);
if (res != 0) {
json_object_put(arr);
return res;
if (backup)
saveFileText(productsFile, backup); /* rollback inventario */
free(backup);
return -1;
}
res = saveJsonArray(fname, arr);
json_object_put(arr);
return (res == 0) ? 0 : -1;
free(backup);
return 0;
}
/* ------------------------------------------------------------------ */
+73 -18
View File
@@ -11,6 +11,8 @@
#define MAX_LEN 64
#define LINE_LEN 96
/*
* Helper
*/
@@ -29,6 +31,15 @@ static void replaceCommaWithDot(char *text) {
*p = '.';
}
static int askAction(const char *title, char **opts, int n_opts) {
WINDOW *win = create_newwin(8, COLS / 3 + 8, LINES / 2 - 4, COLS / 2);
mvwprintw(win, 1, 2, "%s", title);
int choice = uiSelectMenu(win, 3, 2, opts, n_opts);
werase(win);
destroy_win(win);
return choice;
}
static int parseIntValue(const char *text, int *value) {
char *end = NULL;
long parsed = strtol(text, &end, 10);
@@ -216,6 +227,51 @@ static int promptDataFilesForm(char *productsFile, size_t productsFileSize, char
return choice;
}
static int promptExportFileName(char *outFile, size_t outFileSize) {
WINDOW *win = create_newwin(9, COLS / 2, 0, COLS / 2);
char *opts[] = {"Esporta", "Annulla"};
mvwprintw(win, 1, 2, "Esporta vendite a CSV");
mvwprintw(win, 3, 2, "File di destinazione:");
if (outFile && outFile[0])
uiTextInput(win, 4, 2, outFile, (int)outFileSize - 1);
else {
snprintf(outFile, outFileSize, "export.csv");
uiTextInput(win, 4, 2, outFile, (int)outFileSize - 1);
}
int choice = uiSelectMenu(win, 6, 4, opts, 2);
werase(win);
destroy_win(win);
return choice;
}
void runExportCsvWindow() {
char outFile[MAX_LEN + 1];
snprintf(outFile, sizeof outFile, "export.csv");
int choice = promptExportFileName(outFile, sizeof outFile);
if (choice != 1) {
statusf("Operazione annullata");
return;
}
char title[128];
char *opts[] = {"Conferma", "Annulla"};
snprintf(title, sizeof title, "Esportare le vendite in '%s' ?", outFile);
int ok = askAction(title, opts, 2);
if (ok != 1) {
statusf("Operazione annullata");
return;
}
int res = exportSalesToCsv(productPricesFileLocation, countFileLocation, outFile);
if (res == 0)
statusf("Export CSV salvato in '%s'", outFile);
else
warningf("Errore nell'esportazione (codice %d)", res);
}
static char **buildSalesLines(json_object *sales, int *n) {
int count = (int)json_object_array_length(sales);
char **lines = malloc((count > 0 ? count : 1) * sizeof *lines);
@@ -316,15 +372,6 @@ static int listWindow(const char *title, char **lines, int n, const char *footer
return result;
}
static int askAction(const char *title, char **opts, int n_opts) {
WINDOW *win = create_newwin(8, COLS / 3 + 8, LINES / 2 - 4, COLS / 2);
mvwprintw(win, 1, 2, "%s", title);
int choice = uiSelectMenu(win, 3, 2, opts, n_opts);
werase(win);
destroy_win(win);
return choice;
}
static int confirmDeletion(const char *subject) {
char *opts[] = {"Elimina", "Annulla"};
char title[128];
@@ -593,13 +640,16 @@ static void showSaleEditWindow(const char *name, int sold) {
return;
}
int res = updateSold(countFileLocation, name, qty);
int res = updateSold(productPricesFileLocation, countFileLocation, name, qty);
if (res == 0)
statusf("Vendita aggiornata: '%s' = %d", name, qty);
else if (res == 1)
warningf("La vendita '%s' non esiste piu'.", name);
else if (res == 2)
warningf("Stock insufficiente per portare '%s' a %d vendite.", name, qty);
else
warningf("Errore nella scrittura del file '%s'.", countFileLocation);
warningf("Errore nell'aggiornamento di vendite/inventario ('%s', '%s').",
countFileLocation, productPricesFileLocation);
}
static void deleteSaleByName(const char *name) {
@@ -608,13 +658,14 @@ static void deleteSaleByName(const char *name) {
return;
}
int res = deleteSold(countFileLocation, name);
int res = deleteSold(productPricesFileLocation, countFileLocation, name);
if (res == 0)
statusf("Vendita eliminata: '%s'", name);
statusf("Vendita eliminata: '%s' (inventario ripristinato)", name);
else if (res == 1)
warningf("La vendita '%s' non esiste piu'.", name);
else
warningf("Errore nella scrittura del file '%s'.", countFileLocation);
warningf("Errore nell'aggiornamento di vendite/inventario ('%s', '%s').",
countFileLocation, productPricesFileLocation);
}
static void showSaleActionWindow(const char *name, int sold) {
@@ -826,6 +877,9 @@ static void runMainMenuChoice(int choice) {
runOptionsMenu();
break;
case 7:
runExportCsvWindow();
break;
case 8:
runAboutWindow();
break;
}
@@ -836,9 +890,10 @@ int runMainMenu() {
"Segna vendita",
"Vedi vendite",
"Vedi prodotti",
"Aggiungi prodotti",
"Crea nuovo bundle",
"Aggiungi nuovo prodotto",
"Aggiungi nuovo bundle",
"Opzioni",
"Esporta CSV",
"About",
"Exit"
};
@@ -861,11 +916,11 @@ int runMainMenu() {
clrtoeol();
refresh();
if (choice >= 1 && choice <= 7)
if (choice >= 1 && choice <= 8)
runMainMenuChoice(choice);
destroy_win(mainWin);
return choice != 8;
return choice != 9;
}
void runAboutWindow() {