4 Commits
Author SHA1 Message Date
admin fa0e377811 Aggiunti commenti doxygen a include 2026-10-11 13:45:37 +02:00
rexeref defa951e51 pulito file tree 2026-10-10 20:41:45 +02:00
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 609 additions and 44 deletions

No files matched your search

+51
View File
@@ -3,15 +3,66 @@
#include <json-c/json.h> #include <json-c/json.h>
/**
* @brief Verifica se un file esiste sul disco.
* @param fname Percorso del file da controllare.
* @return 1 se il file esiste ed è accessibile, 0 altrimenti.
*/
int existsFile(const char *fname); int existsFile(const char *fname);
/**
* @brief Crea un nuovo file JSON inizializzandolo con un array vuoto ([]).
* @param fname Percorso del file da creare.
* @return 0 in caso di successo, -1 in caso di errore.
*/
int createFile(const char *fname); int createFile(const char *fname);
/**
* @brief Rinomina o sposta un file esistente.
* @param oldName Vecchio percorso/nome del file.
* @param newName Nuovo percorso/nome del file.
* @return 0 in caso di successo, -1 in caso di errore.
*/
int renameDataFile(const char *oldName, const char *newName); int renameDataFile(const char *oldName, const char *newName);
/**
* @brief Carica un array JSON da file. Se il file non esiste, lo crea automaticamente.
* @param fname Percorso del file JSON.
* @return Puntatore all'oggetto JSON (array) caricato, oppure NULL in caso di errore.
*/
json_object *loadJsonArray(const char *fname); json_object *loadJsonArray(const char *fname);
/**
* @brief Salva un array JSON su file formattandolo in modo leggibile (indentato).
* @param fname Percorso del file di destinazione.
* @param arr L'oggetto JSON (array) da salvare.
* @return 0 in caso di successo, -1 in caso di errore.
*/
int saveJsonArray(const char *fname, json_object *arr); int saveJsonArray(const char *fname, json_object *arr);
/**
* @brief Cerca un oggetto all'interno di un array JSON basandosi sul campo "name".
* @param arr L'array JSON in cui effettuare la ricerca.
* @param name La stringa del nome da cercare.
* @return Puntatore all'oggetto JSON trovato, oppure NULL se non esiste.
*/
json_object *findItemByName(json_object *arr, const char *name); json_object *findItemByName(json_object *arr, const char *name);
/**
* @brief Rimuove un oggetto da un array JSON basandosi sul campo "name".
* @param arr L'array JSON da cui rimuovere l'elemento.
* @param name Il nome dell'elemento da eliminare.
* @return 0 in caso di successo, 1 se l'elemento non viene trovato, -1 in caso di errore.
*/
int removeItemByName(json_object *arr, const char *name); int removeItemByName(json_object *arr, const char *name);
/**
* @brief Modifica il campo "name" di un elemento all'interno di un array JSON.
* @param arr L'array JSON contenente l'elemento.
* @param oldName Il nome attuale dell'elemento da rinominare.
* @param newName Il nuovo nome da assegnare.
* @return 0 in caso di successo, 1 se l'elemento non viene trovato, -1 in caso di errore.
*/
int renameItemByName(json_object *arr, const char *oldName, const char *newName); int renameItemByName(json_object *arr, const char *oldName, const char *newName);
#endif #endif
+185 -3
View File
@@ -1,7 +1,6 @@
#ifndef LOGIC_H #ifndef LOGIC_H
#define LOGIC_H #define LOGIC_H
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@@ -16,33 +15,216 @@ struct product {
char name; char name;
}; };
/* ------------------------------------------------------------------ */
/* Prodotti */ /* Prodotti */
/* ------------------------------------------------------------------ */
/**
* @brief Restituisce l'array JSON contenente tutti i prodotti.
* @param fname Percorso del file JSON dei prodotti.
* @return Puntatore all'oggetto JSON (array) dei prodotti (da liberare con json_object_put()), oppure NULL in caso di errore.
*/
json_object *getProducts(const char *fname); json_object *getProducts(const char *fname);
/**
* @brief Stampa a schermo (tramite ncurses) la lista dei prodotti con relativo prezzo, inventario e tag bundle.
* @param fname Percorso del file JSON dei prodotti.
*/
void listProducts(const char *fname); void listProducts(const char *fname);
/**
* @brief Aggiunge un nuovo prodotto con prezzo e inventario illimitato.
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del prodotto.
* @param price Prezzo del prodotto.
* @return 0 in caso di successo, 1 se il prodotto esiste già, -1 in caso di errore.
*/
int addProduct(const char *fname, const char *name, double price); int addProduct(const char *fname, const char *name, double price);
/**
* @brief Aggiunge un nuovo prodotto specificando prezzo e quantità iniziale in magazzino.
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del prodotto.
* @param price Prezzo del prodotto.
* @param stock Quantità iniziale in magazzino (-1 per inventario illimitato).
* @return 0 in caso di successo, 1 se il prodotto esiste già, -1 in caso di errore.
*/
int addProductWithInventory(const char *fname, const char *name, double price, int stock); int addProductWithInventory(const char *fname, const char *name, double price, int stock);
/**
* @brief Aggiunge un prodotto composto (bundle) costituito da altri componenti.
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del bundle.
* @param price Prezzo del bundle.
* @param components Oggetto JSON (array) descrivente i componenti e le relative quantità.
* @return 0 in caso di successo, 1 se il prodotto esiste già, -1 in caso di errore.
*/
int addBundleProduct(const char *fname, const char *name, double price, json_object *components); int addBundleProduct(const char *fname, const char *name, double price, json_object *components);
/**
* @brief Ottiene il prezzo di un prodotto specifico.
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del prodotto da cercare.
* @return Il prezzo del prodotto, oppure -1.0 se non trovato o in caso di errore.
*/
double getPrice(const char *fname, const char *name); double getPrice(const char *fname, const char *name);
/**
* @brief Aggiorna nome e prezzo di un prodotto esistente, sincronizzando anche le vendite se il nome cambia.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param oldName Nome attuale del prodotto.
* @param newName Nuovo nome da assegnare al prodotto.
* @param price Nuovo prezzo del prodotto.
* @return 0 in caso di successo, codice di errore/stato altrimenti.
*/
int updateProduct(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price); int updateProduct(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price);
/**
* @brief Aggiorna nome, prezzo e inventario di un prodotto esistente.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param oldName Nome attuale del prodotto.
* @param newName Nuovo nome da assegnare.
* @param price Nuovo prezzo.
* @param stock Quantità in magazzino.
* @return 0 in caso di successo, codice di errore/stato altrimenti.
*/
int updateProductWithInventory(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price, int stock); int updateProductWithInventory(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price, int stock);
/**
* @brief Legge lo stato dell'inventario (quantità e tracciamento) di un prodotto.
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del prodotto.
* @param stock Puntatore a intero in cui salvare la quantità di magazzino.
* @param tracked Puntatore a intero in cui salvare se l'inventario è tracciato (1) o meno (0).
* @return 0 in caso di successo, 1 se il prodotto non viene trovato, -1 in caso di errore.
*/
int getProductInventory(const char *fname, const char *name, int *stock, int *tracked); int getProductInventory(const char *fname, const char *name, int *stock, int *tracked);
/**
* @brief Imposta la quantità e lo stato di tracciamento dell'inventario per un prodotto.
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del prodotto.
* @param stock Quantità di magazzino da impostare.
* @param tracked Flag di tracciamento (1 per tracciato, 0 altrimenti).
* @return 0 in caso di successo, 1 se il prodotto non è trovato, -1 in caso di errore.
*/
int setProductInventory(const char *fname, const char *name, int stock, int tracked); int setProductInventory(const char *fname, const char *name, int stock, int tracked);
/**
* @brief Consuma (scala) una determinata quantità dall'inventario di un prodotto o dei suoi componenti (se bundle).
* @param fname Percorso del file JSON dei prodotti.
* @param name Nome del prodotto.
* @param qty Quantità da consumare.
* @return 0 in caso di successo, codice di errore o disponibilità insufficiente altrimenti.
*/
int consumeProductInventory(const char *fname, const char *name, int qty); int consumeProductInventory(const char *fname, const char *name, int qty);
/* ------------------------------------------------------------------ */
/* Copie vendute */ /* Copie vendute */
/* ------------------------------------------------------------------ */
/**
* @brief Restituisce il numero di copie vendute di un determinato prodotto.
* @param fname Percorso del file JSON delle vendite.
* @param name Nome del prodotto.
* @return Il numero di copie vendute (0 se non presente, -1 in caso di errore).
*/
int getCount(const char *fname, const char *name); int getCount(const char *fname, const char *name);
/**
* @brief Stampa a schermo (tramite ncurses) la lista delle vendite registrate per ciascun prodotto.
* @param fname Percorso del file JSON delle vendite.
*/
void listSold(const char *fname); void listSold(const char *fname);
/**
* @brief Aggiunge una quantità di copie vendute per un prodotto (creando la voce se non esiste).
* @param fname Percorso del file JSON delle vendite.
* @param name Nome del prodotto.
* @param qty Quantità venduta da aggiungere.
* @return 0 in caso di successo, -1 in caso di errore.
*/
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 *fname, const char *name); /**
* @brief Elimina la registrazione delle vendite di un prodotto e ripristina l'inventario corrispondente.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param name Nome del prodotto di cui eliminare le vendite.
* @return 0 in caso di successo, 1 se la vendita non è presente, -1 o codice di errore in caso di fallimento.
*/
int deleteSold(const char *productsFile, const char *soldFile, const char *name);
/**
* @brief Aggiorna la quantità di copie vendute per un prodotto, regolando di conseguenza il magazzino.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param name Nome del prodotto.
* @param qty Nuova quantità totale venduta.
* @return 0 in caso di successo, 1 se non presente, codice di errore altrimenti.
*/
int updateSold(const char *productsFile, const char *soldFile, const char *name, int qty);
/**
* @brief Registra una vendita scalando l'inventario dei prodotti e incrementando le vendite, con supporto di rollback in caso di problemi.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param name Nome del prodotto venduto.
* @param qty Quantità venduta.
* @return 0 in caso di successo, codice di errore in caso di fallimento.
*/
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 */
/* ------------------------------------------------------------------ */
/**
* @brief Calcola le entrate totali sommando il prodotto tra prezzo e copie vendute per ciascun articolo.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @return Il totale delle entrate, oppure -1.0 in caso di errore.
*/
double getTotalRevenue(const char *productsFile, const char *soldFile); double getTotalRevenue(const char *productsFile, const char *soldFile);
/* ------------------------------------------------------------------ */
/* Export */
/* ------------------------------------------------------------------ */
/**
* @brief Esporta i dati delle vendite e dei prodotti in un file in formato CSV.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param outFile Percorso del file CSV di output.
* @return 0 in caso di successo, un valore negativo in caso di errore.
*/
int exportSalesToCsv(const char *productsFile, const char *soldFile, const char *outFile);
/* ------------------------------------------------------------------ */
/* Config */ /* Config */
/* ------------------------------------------------------------------ */
/**
* @brief Assicura che il file di configurazione dell'applicazione esista, creandolo se necessario.
* @param fname Percorso del file di configurazione.
* @return 0 in caso di successo, codice di errore altrimenti.
*/
int ensureAppConfig(const char *fname); int ensureAppConfig(const char *fname);
/**
* @brief Carica la configurazione dell'applicazione dal file specificato.
* @param fname Percorso del file di configurazione.
* @return 0 in caso di successo, -1 in caso di errore.
*/
int loadAppConfig(const char *fname); int loadAppConfig(const char *fname);
/**
* @brief Salva la configurazione corrente dell'applicazione sul file specificato.
* @param fname Percorso del file di configurazione.
* @return 0 in caso di successo, -1 in caso di errore.
*/
int saveAppConfig(const char *fname); int saveAppConfig(const char *fname);
#endif #endif
+77
View File
@@ -6,18 +6,95 @@
#include <string.h> #include <string.h>
#include <ncurses.h> #include <ncurses.h>
/**
* @brief Crea e restituisce una nuova finestra ncurses con i bordi tracciati.
* @param height Altezza della finestra.
* @param width Larghezza della finestra.
* @param starty Coordinata Y iniziale sullo schermo.
* @param startx Coordinata X iniziale sullo schermo.
* @return Puntatore all'oggetto WINDOW creato.
*/
WINDOW *create_newwin(int height, int width, int starty, int startx); WINDOW *create_newwin(int height, int width, int starty, int startx);
/**
* @brief Distrugge una finestra ncurses creata in precedenza e libera la memoria associata.
* @param local_win Puntatore alla finestra da eliminare.
*/
void destroy_win(WINDOW *local_win); void destroy_win(WINDOW *local_win);
/**
* @brief Stampa del testo all'interno di una finestra gestendo l'andata a capo automatica (word wrapping) per parola.
* @param win Finestra in cui stampare.
* @param start_y Coordinata Y iniziale all'interno della finestra.
* @param start_x Coordinata X iniziale all'interno della finestra.
* @param str Stringa di testo da stampare.
*/
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str); void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str);
/**
* @brief Gestisce un interruttore grafico interattivo (stato ON/OFF) modificabile da tastiera.
* @param win Finestra di visualizzazione.
* @param y Coordinata Y dell'interruttore.
* @param x Coordinata X dell'interruttore.
* @param state Stato iniziale (1 per ON, 0 per OFF).
* @return Lo stato finale dell'interruttore dopo l'interazione dell'utente.
*/
int uiToggleSwitch(WINDOW *win, int y, int x, int state); int uiToggleSwitch(WINDOW *win, int y, int x, int state);
/**
* @brief Gestisce un campo di input di testo interattivo all'interno di una finestra ncurses.
* @param win Finestra in cui inserire il testo.
* @param y Coordinata Y.
* @param x Coordinata X.
* @param input Buffer contenente l'eventuale testo iniziale e in cui memorizzare il risultato digitato.
* @param max_len Lunghezza massima consentita per l'input.
*/
void uiTextInput(WINDOW *win, int y, int x, char *input, int max_len); void uiTextInput(WINDOW *win, int y, int x, char *input, int max_len);
/**
* @brief Mostra un menu di scelta multipla navigabile da tastiera tramite frecce o tasti vim (j/k).
* @param win Finestra in cui mostrare il menu.
* @param start_y Coordinata Y iniziale.
* @param start_x Coordinata X iniziale.
* @param options Array di stringhe contenente le opzioni del menu.
* @param n_options Numero totale di opzioni disponibili.
* @return L'indice (1-based) della voce selezionata dall'utente.
*/
int uiSelectMenu(WINDOW *win, int start_y, int start_x, char **options, int n_options); int uiSelectMenu(WINDOW *win, int start_y, int start_x, char **options, int n_options);
/**
* @brief Stampa un menu all'interno di una finestra evidenziando una voce specifica.
* @param menu_win Finestra del menu.
* @param y Coordinata Y.
* @param x Coordinata X.
* @param highlight Indice della voce da evidenziare graficamente.
*/
void print_menu(WINDOW *menu_win, int y, int x, int highlight); void print_menu(WINDOW *menu_win, int y, int x, int highlight);
/**
* @brief Mostra una finestra di avviso (warning) utilizzando una stringa formattata (stile printf).
* @param fmt Stringa di formattazione.
* @param ... Argomenti variabili da formattare.
*/
void warningf(const char *fmt, ...); void warningf(const char *fmt, ...);
/**
* @brief Apre una finestra di popup con un messaggio di avviso e attende la pressione di un tasto per chiudersi.
* @param string Messaggio di errore o avviso da mostrare.
*/
void warning(char *string); void warning(char *string);
/**
* @brief Aggiorna la barra di stato in basso sullo schermo con un messaggio formattato (stile printf).
* @param fmt Stringa di formattazione.
* @param ... Argomenti variabili da formattare.
*/
void statusf(const char *fmt, ...); void statusf(const char *fmt, ...);
/**
* @brief Scrive un messaggio direttamente nella barra di stato in basso sullo schermo.
* @param msg Messaggio da visualizzare nella status bar.
*/
void status(char *msg); void status(char *msg);
#endif #endif
+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
+1 -1
View File
@@ -36,6 +36,6 @@ bundle: $(OBJECTS)
@rm -fr dist/ @rm -fr dist/
clean: clean:
rm -rf $(BUILD_DIR) $(EXEC) dist *.json rm -rf $(BUILD_DIR) $(EXEC) dist *.json .fpm
.PHONY: all clean bundle .PHONY: all clean bundle
+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() {