Compare commits

2 Commits
Author SHA1 Message Date
rexeref c088caf03e rimosso .fpm dal tree 2026-10-10 20:40:49 +02:00
rexeref 8cd79da5eb Aggiunta gestione export csv 2026-10-10 20:40:24 +02:00
7 changed files with 372 additions and 42 deletions

No files matched your search

+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); int getCount(const char *fname, const char *name);
void listSold(const char *fname); void listSold(const char *fname);
int addSold(const char *fname, const char *name, int qty); int addSold(const char *fname, const char *name, int qty);
int updateSold(const char *fname, const char *name, int qty); int deleteSold(const char *productsFile, const char *soldFile, const char *name);
int deleteSold(const char *fname, 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); int recordSale(const char *productsFile, const char *soldFile, const char *name, int qty);
/* Entrate */ /* Entrate */
double getTotalRevenue(const char *productsFile, const char *soldFile); double getTotalRevenue(const char *productsFile, const char *soldFile);
/* Export */
int exportSalesToCsv(const char *productsFile, const char *soldFile, const char *outFile);
/* Config */ /* Config */
int ensureAppConfig(const char *fname); int ensureAppConfig(const char *fname);
int loadAppConfig(const char *fname); int loadAppConfig(const char *fname);
+1
View File
@@ -14,6 +14,7 @@ void runAboutWindow();
void runViewSalesWindow(); void runViewSalesWindow();
void runViewProductsWindow(); void runViewProductsWindow();
void runAddProductWindow(); void runAddProductWindow();
void runExportCsvWindow();
void example_window(); void example_window();
#endif #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 <json-c/json.h>
#include "../include/logic.h" #include "../include/logic.h"
#include <stdio.h>
#include "../include/globals.h" #include "../include/globals.h"
#include "../include/jsonFile.h" #include "../include/jsonFile.h"
@@ -39,6 +41,90 @@ static int readInventory(json_object *item, int *stock, int *tracked) {
return 0; 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) { static void writeInventory(json_object *item, int stock, int tracked) {
if (!item) if (!item)
return; return;
@@ -267,6 +353,46 @@ static int consumeTree(json_object *products, json_object *item, int qty) {
return 0; 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) { static char *loadFileText(const char *fname) {
FILE *f = fopen(fname, "rb"); FILE *f = fopen(fname, "rb");
long size; long size;
@@ -692,38 +818,111 @@ int recordSale(const char *productsFile, const char *soldFile, const char *name,
return 0; return 0;
} }
int updateSold(const char *fname, const char *name, int qty) { int deleteSold(const char *productsFile, const char *soldFile, const char *name) {
json_object *arr = loadJsonArray(fname); json_object *sales = loadJsonArray(soldFile);
if (!arr) if (!sales)
return -1; return -1;
json_object *item = findItemByName(arr, name); json_object *item = findItemByName(sales, name);
if (!item) { if (!item) {
json_object_put(arr); json_object_put(sales);
return 1; 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)); 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) { if (res != 0) {
json_object_put(arr); if (backup)
return res; saveFileText(productsFile, backup); /* rollback inventario */
free(backup);
return -1;
} }
res = saveJsonArray(fname, arr); free(backup);
json_object_put(arr); return 0;
return (res == 0) ? 0 : -1;
} }
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
+73 -18
View File
@@ -11,6 +11,8 @@
#define MAX_LEN 64 #define MAX_LEN 64
#define LINE_LEN 96 #define LINE_LEN 96
/* /*
* Helper * Helper
*/ */
@@ -29,6 +31,15 @@ static void replaceCommaWithDot(char *text) {
*p = '.'; *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) { static int parseIntValue(const char *text, int *value) {
char *end = NULL; char *end = NULL;
long parsed = strtol(text, &end, 10); long parsed = strtol(text, &end, 10);
@@ -216,6 +227,51 @@ static int promptDataFilesForm(char *productsFile, size_t productsFileSize, char
return choice; 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) { static char **buildSalesLines(json_object *sales, int *n) {
int count = (int)json_object_array_length(sales); int count = (int)json_object_array_length(sales);
char **lines = malloc((count > 0 ? count : 1) * sizeof *lines); 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; 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) { static int confirmDeletion(const char *subject) {
char *opts[] = {"Elimina", "Annulla"}; char *opts[] = {"Elimina", "Annulla"};
char title[128]; char title[128];
@@ -593,13 +640,16 @@ static void showSaleEditWindow(const char *name, int sold) {
return; return;
} }
int res = updateSold(countFileLocation, name, qty); int res = updateSold(productPricesFileLocation, countFileLocation, name, qty);
if (res == 0) if (res == 0)
statusf("Vendita aggiornata: '%s' = %d", name, qty); statusf("Vendita aggiornata: '%s' = %d", name, qty);
else if (res == 1) else if (res == 1)
warningf("La vendita '%s' non esiste piu'.", name); warningf("La vendita '%s' non esiste piu'.", name);
else if (res == 2)
warningf("Stock insufficiente per portare '%s' a %d vendite.", name, qty);
else 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) { static void deleteSaleByName(const char *name) {
@@ -608,13 +658,14 @@ static void deleteSaleByName(const char *name) {
return; return;
} }
int res = deleteSold(countFileLocation, name); int res = deleteSold(productPricesFileLocation, countFileLocation, name);
if (res == 0) if (res == 0)
statusf("Vendita eliminata: '%s'", name); statusf("Vendita eliminata: '%s' (inventario ripristinato)", name);
else if (res == 1) else if (res == 1)
warningf("La vendita '%s' non esiste piu'.", name); warningf("La vendita '%s' non esiste piu'.", name);
else 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) { static void showSaleActionWindow(const char *name, int sold) {
@@ -826,6 +877,9 @@ static void runMainMenuChoice(int choice) {
runOptionsMenu(); runOptionsMenu();
break; break;
case 7: case 7:
runExportCsvWindow();
break;
case 8:
runAboutWindow(); runAboutWindow();
break; break;
} }
@@ -836,9 +890,10 @@ int runMainMenu() {
"Segna vendita", "Segna vendita",
"Vedi vendite", "Vedi vendite",
"Vedi prodotti", "Vedi prodotti",
"Aggiungi prodotti", "Aggiungi nuovo prodotto",
"Crea nuovo bundle", "Aggiungi nuovo bundle",
"Opzioni", "Opzioni",
"Esporta CSV",
"About", "About",
"Exit" "Exit"
}; };
@@ -861,11 +916,11 @@ int runMainMenu() {
clrtoeol(); clrtoeol();
refresh(); refresh();
if (choice >= 1 && choice <= 7) if (choice >= 1 && choice <= 8)
runMainMenuChoice(choice); runMainMenuChoice(choice);
destroy_win(mainWin); destroy_win(mainWin);
return choice != 8; return choice != 9;
} }
void runAboutWindow() { void runAboutWindow() {