3 Commits
Author SHA1 Message Date
admin 6fb76ebed6 Patch a funzionamento registrazione vendite 2026-10-11 14:29:14 +02:00
admin c670aceafb Aggiustata scritta roma2lug nel logo 2026-10-11 14:00:14 +02:00
admin fa0e377811 Aggiunti commenti doxygen a include 2026-10-11 13:45:37 +02:00
7 changed files with 754 additions and 692 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
+143 -13
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,36 +15,167 @@ struct product {
char name; char name;
}; };
/* Prodotti */ /*
json_object *getProducts(const char *fname); * Modello dati: nel file prodotti si salva solo la giacenza INIZIALE ("initial").
void listProducts(const char *fname); * Lo stock corrente e' sempre calcolato: initial - copie vendute
int addProduct(const char *fname, const char *name, double price); * (comprese quelle consumate tramite bundle). Le vendite scrivono solo il file vendite.
int addProductWithInventory(const char *fname, const char *name, double price, int stock); */
int addBundleProduct(const char *fname, const char *name, double price, json_object *components);
double getPrice(const char *fname, const char *name);
int updateProduct(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price);
int updateProductWithInventory(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price, int stock);
int getProductInventory(const char *fname, const char *name, int *stock, int *tracked);
int setProductInventory(const char *fname, const char *name, int stock, int tracked);
int consumeProductInventory(const char *fname, const char *name, int qty);
/* ------------------------------------------------------------------ */
/* Prodotti */
/* ------------------------------------------------------------------ */
/**
* @brief Restituisce l'array JSON contenente tutti i prodotti, cosi' come salvati su file
* (quindi con "initial" e senza stock corrente).
* @param fname Percorso del file JSON dei prodotti.
* @return Array JSON (da liberare con json_object_put()), oppure NULL in caso di errore.
*/
json_object *getProducts(const char *fname);
/**
* @brief Come getProducts, ma ogni prodotto ha in piu' il campo "stock" calcolato
* (-1 = infinito; i bundle hanno sempre -1). E' una vista in memoria: non va salvata.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite (se manca, si assume nessuna vendita).
* @return Array JSON (da liberare con json_object_put()), oppure NULL in caso di errore.
*/
json_object *getProductsWithStock(const char *productsFile, const char *soldFile);
/**
* @brief Aggiunge un nuovo prodotto con prezzo e inventario illimitato.
* @return 0 in caso di successo, 1 se il prodotto esiste gia', -1 in caso di errore.
*/
int addProduct(const char *fname, const char *name, double price);
/**
* @brief Aggiunge un nuovo prodotto specificando prezzo e giacenza iniziale.
* @param stock Giacenza iniziale (negativo per inventario illimitato).
* @return 0 in caso di successo, 1 se il prodotto esiste gia', -1 in caso di errore.
*/
int addProductWithInventory(const char *fname, const char *name, double price, int stock);
/**
* @brief Aggiunge un prodotto composto (bundle).
* @param components Array JSON di {"name": ..., "qty": ...}; il bundle ne prende possesso in caso di successo.
* @return 0 in caso di successo, 1 se il prodotto esiste gia', -1 in caso di errore.
*/
int addBundleProduct(const char *fname, const char *name, double price, json_object *components);
/**
* @brief Ottiene il prezzo di un prodotto.
* @return Il prezzo, oppure -1.0 se non trovato o in caso di errore.
*/
double getPrice(const char *fname, const char *name);
/**
* @brief Aggiorna nome e prezzo di un prodotto lasciando invariato lo stock corrente.
* @return 0 ok, 1 non trovato, 2 nome gia' usato, -1 errore.
*/
int updateProduct(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price);
/**
* @brief Aggiorna nome, prezzo e stock di un prodotto.
* Se il nome cambia aggiorna nella stessa operazione file vendite e riferimenti nei bundle.
* @param stock Giacenza ATTUALE desiderata (negativo = infinita).
* @return 0 ok, 1 non trovato, 2 nome gia' usato, -1 errore.
*/
int updateProductWithInventory(const char *productsFile, const char *soldFile, const char *oldName, const char *newName, double price, int stock);
/**
* @brief Legge lo stock corrente (calcolato) di un prodotto.
* @param productsFile Percorso del file JSON dei prodotti.
* @param soldFile Percorso del file JSON delle vendite.
* @param name Nome del prodotto.
* @param stock Quantita' corrente (PRODUCT_STOCK_UNLIMITED se non tracciato).
* @param tracked 1 se l'inventario e' tracciato, 0 altrimenti.
* @return 0 ok, 1 prodotto non trovato, -1 errore.
*/
int getProductInventory(const char *productsFile, const char *soldFile, const char *name, int *stock, int *tracked);
/* ------------------------------------------------------------------ */
/* Copie vendute */ /* Copie vendute */
/* ------------------------------------------------------------------ */
/**
* @brief Numero di copie vendute di un prodotto.
* @return 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 (ncurses) le vendite registrate per ciascun prodotto.
*/
void listSold(const char *fname); void listSold(const char *fname);
/**
* @brief Aggiunge copie vendute per un prodotto (crea la voce se non esiste).
* Non controlla lo stock: per questo usare recordSale().
* @return 0 ok, -1 errore.
*/
int addSold(const char *fname, const char *name, int qty); int addSold(const char *fname, const char *name, int qty);
/**
* @brief Elimina la registrazione delle vendite di un prodotto.
* Lo stock si "ripristina" da solo perche' e' derivato.
* @param productsFile Non usato, mantenuto per compatibilita'.
* @return 0 ok, 1 vendita non presente, -1 errore.
*/
int deleteSold(const char *productsFile, const char *soldFile, const char *name); int deleteSold(const char *productsFile, const char *soldFile, const char *name);
/**
* @brief Imposta le copie vendute di un prodotto; se aumentano controlla la disponibilita'.
* @return 0 ok, 1 vendita non presente, 2 scorta insufficiente, -1 errore.
*/
int updateSold(const char *productsFile, const char *soldFile, const char *name, int qty); int updateSold(const char *productsFile, const char *soldFile, const char *name, int qty);
/**
* @brief Registra una vendita dopo aver verificato la disponibilita' (anche dei componenti, se bundle).
* Scrive solo il file vendite.
* @return 0 ok, 1 prodotto inesistente, 2 scorta insufficiente, -1 errore.
*/
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 Entrate totali: somma di prezzo * copie vendute.
* @return Il totale, oppure -1.0 in caso di errore.
*/
double getTotalRevenue(const char *productsFile, const char *soldFile); double getTotalRevenue(const char *productsFile, const char *soldFile);
/* ------------------------------------------------------------------ */
/* Export */ /* Export */
/* ------------------------------------------------------------------ */
/**
* @brief Esporta prodotti e vendite in CSV (separatore ';').
* @return 0 ok, -1 errore di lettura prodotti, -2 errore di apertura del file di destinazione.
*/
int exportSalesToCsv(const char *productsFile, const char *soldFile, const char *outFile); int exportSalesToCsv(const char *productsFile, const char *soldFile, const char *outFile);
/* ------------------------------------------------------------------ */
/* Config */ /* Config */
/* ------------------------------------------------------------------ */
/**
* @brief Crea il file di configurazione se non esiste.
* @return 0 ok, altro in caso di errore.
*/
int ensureAppConfig(const char *fname); int ensureAppConfig(const char *fname);
/**
* @brief Carica la configurazione dal file specificato.
* @return 0 ok, -1 errore.
*/
int loadAppConfig(const char *fname); int loadAppConfig(const char *fname);
/**
* @brief Salva la configurazione corrente sul file specificato.
* @return 0 ok, -1 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 -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
+476 -672
View File
File diff suppressed because it is too large. Load diff
+1 -1
View File
@@ -61,6 +61,6 @@ int main(){
void printLogoBG(){ void printLogoBG(){
wwrap_print(stdscr, LINES / 2 - 3, COLS / 2 - 5," ######### \n ################### \n ######## ##### \n ####### ### \n ###### ### \n ###### ### ## \n ############ # ### ## \n ################ ### # \n ############ ##### # \n ############## \n ######### \n ## \n ####### \n ## ## \n ## ## ###### ## ## # ## ## \n ###### ##### ######## #### ## ## ## # ## \n ## ## ## ## ## ## ## ##### ## ## ## # ## ## \n ## ## #### ## ## ## #### ####### ##### ##### ##### "); wwrap_print(stdscr, LINES / 2 - 3, COLS / 2 - 5, " ######### \n ################### \n ######## ##### \n ####### ### \n ###### ### \n ###### ### ## \n ############ # ### ## \n ################ ### # \n ############ ##### # \n ############## \n ######### \n ## \n ####### \n ###### # ## \n ## ## ###### ## ## # ##### \n ###### ##### ######## #### ## ## ## # ## \n ## ## ## ## ## ## ## ## ## ## ## ## # ## ## \n ## ## #### ## ## ## ##### ####### ##### ##### #####");
mvprintw(LINES -1, COLS / 2 - 25, "Product Manager by Vincenzo Nikodem Cesqui Podiani"); mvprintw(LINES -1, COLS / 2 - 25, "Product Manager by Vincenzo Nikodem Cesqui Podiani");
} }
+5 -5
View File
@@ -764,7 +764,7 @@ static void showCreateBundleWindow(void) {
return; return;
} }
json_object *products = getProducts(productPricesFileLocation); json_object *products = getProductsWithStock(productPricesFileLocation, countFileLocation);
if (!products) { if (!products) {
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation); warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
return; return;
@@ -927,7 +927,7 @@ void runAboutWindow() {
WINDOW *aboutWin; WINDOW *aboutWin;
aboutWin = create_newwin(LINES / 2 - 2, COLS / 2, LINES / 2, 0); aboutWin = create_newwin(LINES / 2 - 2, COLS / 2, LINES / 2, 0);
wwrap_print(aboutWin, 1, 1, "Questo e' un software fatto per Roma2LUG, il codice verra' pubblicato sul gitea del lug in futuro. La parte logica e' fatta con Claude Code, non avevo voglia di scrivere quei pezzi e volevo effettivamente rendere utile il codice che avevo scritto per gestire ncurses. Oh well, spero vi possa essere comunque utile. Buon lavoro / Divertimento!"); wwrap_print(aboutWin, 1, 1, "Questo e' un software fatto per Roma2LUG, il codice verra' pubblicato sul gitea del lug in futuro.\nLa parte logica e' fatta con Claude Code, non avevo voglia di scrivere quei pezzi e volevo effettivamente rendere utile il codice che avevo scritto per gestire ncurses.\nOh well, spero vi possa essere comunque utile.\nBuon lavoro / Divertimento!");
statusf("Uovo di pasqua ()"); statusf("Uovo di pasqua ()");
wrefresh(aboutWin); wrefresh(aboutWin);
getch(); getch();
@@ -976,7 +976,7 @@ void runOptionsMenu() {
/* ------------------------------------------------------------------ */ /* ------------------------------------------------------------------ */
void runSellWindow() { void runSellWindow() {
json_object *products = getProducts(productPricesFileLocation); json_object *products = getProductsWithStock(productPricesFileLocation, countFileLocation);
if (!products) { if (!products) {
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation); warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
return; return;
@@ -1031,7 +1031,7 @@ void runSellWindow() {
if (res == 0) { if (res == 0) {
int stock = 0; int stock = 0;
int tracked = 0; int tracked = 0;
if (getProductInventory(productPricesFileLocation, name, &stock, &tracked) == 0 && tracked) if (getProductInventory(productPricesFileLocation, countFileLocation, name, &stock, &tracked) == 0 && tracked)
statusf("Registrata vendita: %d x '%s' = %.2f (rimasti: %d)", qty, name, qty * price, stock); statusf("Registrata vendita: %d x '%s' = %.2f (rimasti: %d)", qty, name, qty * price, stock);
else else
statusf("Registrata vendita: %d x '%s' = %.2f", qty, name, qty * price); statusf("Registrata vendita: %d x '%s' = %.2f", qty, name, qty * price);
@@ -1103,7 +1103,7 @@ void runViewSalesWindow() {
void runViewProductsWindow() { void runViewProductsWindow() {
while (1) { while (1) {
json_object *products = getProducts(productPricesFileLocation); json_object *products = getProductsWithStock(productPricesFileLocation, countFileLocation);
if (!products) { if (!products) {
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation); warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
return; return;