28 Commits
Author SHA1 Message Date
admin 176c2d8be5 Aggiunta creazione pacchetto tar di distribuzione 2026-10-09 17:27:33 +02:00
admin 5db402c7cf aggiunta logica di gestione dati. Assistito con ai 2026-10-09 17:18:38 +02:00
admin 1a15d93f76 Merge branch 'main' of git.lug.uniroma2.it:rexeref/productManager 2026-05-29 22:15:26 +02:00
admin 0471375aab commentato 2026-05-29 22:14:52 +02:00
rexeref 2427831b9b Update README.md 2026-05-29 21:36:55 +02:00
admin 42f3b43a68 Aggiunto README.md 2026-05-29 21:36:01 +02:00
admin c69c420781 Code refactor 2026-05-29 21:10:06 +02:00
admin 673b58caec io ODIO vscode 2026-05-29 17:38:43 +02:00
admin 32365b9617 Pulito build/* 2026-05-29 17:25:12 +02:00
admin 90b4e7db00 Merge branch 'main' of git.lug.uniroma2.it:rexeref/productManager 2026-05-29 17:24:08 +02:00
admin e4f307f183 Correzioni varie in gestione compilazione 2026-05-29 17:01:02 +02:00
admin fac33176ed rimosso dal branch il file di samuele 2025-10-31 15:40:55 +01:00
admin e8d5e7d2b1 aggiunto file .gitignore 2025-10-31 14:37:16 +01:00
Moretti a0314628bd rimosso file inutile come me 2025-10-31 14:34:27 +01:00
admin b5d9009ce1 Test spiegazione francesco 2025-10-31 14:34:27 +01:00
admin 7eee20414c Controllato funzionamento existsFile() createFile() in logic.c 2025-10-31 14:34:27 +01:00
admin d82b5466b0 Rimossi file inutili 2025-10-31 14:34:27 +01:00
admin 7c7859dc90 Commit di recap 2025-10-31 14:34:27 +01:00
admin 2f3ef9736c 14-10-25 2025-10-31 14:34:27 +01:00
admin 07c7235831 Cambiata estensione file di lettura da txt a fpm 2025-10-31 14:34:27 +01:00
admin f18d0cf043 Preparato ambiente di testing allo sviluppo 2025-10-31 14:34:27 +01:00
admin 619774c809 Aggiunto ambiente di testing funzioni slegate a ncurses 2025-10-31 14:34:27 +01:00
admin 29c375b20c Aggiunto logo all'avvio 2025-10-31 14:34:27 +01:00
admin 750f7a9149 Aggiustati dettagli estetici e cambiate funzioni di print varie 2025-10-31 14:34:27 +01:00
admin 5508532619 Ristrutturato in più file 2025-10-31 14:34:27 +01:00
admin 499b3787c6 Pierosforo 2025-10-31 14:34:27 +01:00
admin 4d9eba1512 primo commit 2025-10-31 14:34:27 +01:00
morels 99d5e5244c Orribile prima versione scritta da Samuele 2025-06-12 17:24:13 +02:00
26 changed files with 1090 additions and 369 deletions

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
build/
.vscode/
productManager
test
-11
View File
@@ -1,11 +0,0 @@
{
"makefile.configureOnOpen": false,
"files.associations": {
"ncurses.h": "c",
"logic.h": "c",
"stdio.h": "c",
"globals.h": "c",
"windows.h": "c",
"stdlib.h": "c"
}
}
+11
View File
@@ -0,0 +1,11 @@
# Product Manager
Un gestore di inventario e vendite minimale
## Scaricarlo
Disponibile solo per linux.
Scarica l'ultima versione da [qui](https://git.lug.uniroma2.it/rexeref/productManager/releases)!
Avvia l'eseguibile da un terminale.
Se non si avvia rendilo prima un eseguibile con chmod +x
***questo codice molto probabilmente NON è sicuro, utilizzarlo a propria indiscrezione***
-2
View File
@@ -1,2 +0,0 @@
build/globals.o: src/globals.c src/../include/globals.h
src/../include/globals.h:
BIN
View File
Binary file not shown.
-4
View File
@@ -1,4 +0,0 @@
build/logic.o: src/logic.c src/../include/logic.h \
src/../include/globals.h
src/../include/logic.h:
src/../include/globals.h:
BIN
View File
Binary file not shown.
-3
View File
@@ -1,3 +0,0 @@
build/main.o: src/main.c include/windows.h include/logic.h
include/windows.h:
include/logic.h:
BIN
View File
Binary file not shown.
-4
View File
@@ -1,4 +0,0 @@
build/testLogic.o: src/testLogic.c src/../include/logic.h \
src/../include/globals.h
src/../include/logic.h:
src/../include/globals.h:
BIN
View File
Binary file not shown.
-5
View File
@@ -1,5 +0,0 @@
build/windows.o: src/windows.c src/../include/windows.h \
src/../include/logic.h src/../include/globals.h
src/../include/windows.h:
src/../include/logic.h:
src/../include/globals.h:
BIN
View File
Binary file not shown.
+1
View File
@@ -6,5 +6,6 @@
#include <string.h>
extern char productPricesFileLocation[64]; // Dichiarazione extern
extern char countFileLocation[64];
#endif
+19 -2
View File
@@ -1,10 +1,12 @@
#ifndef LOGIC_H
#define LOGIC_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include <json-c/json.h>
struct product {
float cost;
@@ -12,8 +14,23 @@ struct product {
char name;
};
int getCount();
/* File */
int existsFile(const char *fname);
void createFile(const char *fname);
int createFile(const char *fname);
int renameDataFile(const char *oldName, const char *newName);
/* Prodotti */
json_object *getProducts(const char *fname);
void listProducts(const char *fname);
int addProduct(const char *fname, const char *name, double price);
double getPrice(const char *fname, const char *name);
/* Copie vendute */
int getCount(const char *fname, const char *name);
void listSold(const char *fname);
int addSold(const char *fname, const char *name, int qty);
/* Entrate */
double getTotalRevenue(const char *productsFile, const char *soldFile);
#endif
+11 -8
View File
@@ -1,20 +1,23 @@
#ifndef WINDOWS_H
#define WINDOWS_H
#ifndef WINDOWSBACK_H
#define WINDOWSBACK_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
int runMainMenu();
void runOptionsMenu();
WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str);
int uiToggleSwitch(WINDOW *win, int y, int x, int state);
void uiTextInput(WINDOW *win, int y, int x, char *input, int max_len);
int uiSelectMenu(WINDOW *win, int start_y, int start_x, char **options, int n_options);
void print_menu(WINDOW *menu_win, int y, int x, int highlight);
void warningf(const char *fmt, ...);
void warning(char *string);
void statusf(const char *fmt, ...);
void status(char *msg);
WINDOW *create_newwin(int height, int width, int starty, int startx);
void destroy_win(WINDOW *local_win);
void print_menu(WINDOW *menu_win, int y, int x, int highlight);
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str);
#endif
+18
View File
@@ -0,0 +1,18 @@
#ifndef WINDOWSDECLARATION_H
#define WINDOWSDECLARATION_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
int runMainMenu();
void runOptionsMenu();
void runSellWindow();
void runAboutWindow();
void runViewSalesWindow();
void runViewProductsWindow();
void runAddProductWindow();
void example_window();
#endif
+21 -10
View File
@@ -1,17 +1,21 @@
CC = gcc
CFLAGS = -I./include -Wall -Wextra -MMD -MP
LDLIBS = -lncurses
CC ?= gcc
PKG_CONFIG ?= pkg-config
CFLAGS = -I./include -Wall -Wextra -MMD -MP $(shell $(PKG_CONFIG) --cflags json-c ncurses)
LDLIBS = $(shell $(PKG_CONFIG) --libs json-c ncurses)
BUNDLE_LDFLAGS = -Wl,--dynamic-linker=/lib64/ld-linux-x86-64.so.2 -Wl,-rpath,'$$ORIGIN/lib'
SRC_DIR = src
BUILD_DIR = build
SOURCES = $(wildcard $(SRC_DIR)/*.c)
SOURCES = $(filter-out $(SRC_DIR)/testLogic.c, $(wildcard $(SRC_DIR)/*.c))
OBJECTS = $(patsubst $(SRC_DIR)/%.c,$(BUILD_DIR)/%.o,$(SOURCES))
EXEC = productManager
EXECTEST = test
DEPS = $(OBJECTS:.o=.d)
all: $(EXEC)
# 1. Per testare in locale su NixOS
$(EXEC): $(OBJECTS)
$(CC) $^ -o $@ $(LDLIBS)
@@ -21,10 +25,17 @@ $(BUILD_DIR)/%.o: $(SRC_DIR)/%.c
-include $(DEPS)
test:
$(CC) $(CFLAGS) src/logic.c src/testLogic.c -o $(EXECTEST)
# 2. Per creare il pacchetto portabile da inviare
bundle: $(OBJECTS)
@mkdir -p dist/lib
$(CC) $^ -o dist/$(EXEC) $(BUNDLE_LDFLAGS) $(LDLIBS)
@cp -L $$( $(PKG_CONFIG) --variable=libdir json-c )/libjson-c.so* dist/lib/
@cp -L $$( $(PKG_CONFIG) --variable=libdir ncurses )/libncurses*.so* dist/lib/
@tar czf productManager.tar.gz --directory dist/ .
@echo "=== Pacchetto portabile creato con successo in productManager.tar.gz ==="
@rm -fr dist/
clean:
rm -rf $(BUILD_DIR) $(EXEC)
rm -rf $(BUILD_DIR) $(EXEC) dist *.json
.PHONY: all clean
.PHONY: all clean bundle
+5 -1
View File
@@ -1,8 +1,12 @@
{ pkgs ? import <nixpkgs> {} }:
pkgs.mkShell {
buildInputs = [
nativeBuildInputs = [
pkgs.gcc
pkgs.gnumake
pkgs.pkgconf
];
buildInputs = [
pkgs.ncurses
pkgs.json_c
];
}
+3 -2
View File
@@ -1,4 +1,5 @@
#include "../include/globals.h"
char productPricesFileLocation[64]="./productPricesSettings.fpm";
char countFileLocation[64]="./count.fpm";
char productPricesFileLocation[64]="./pm-item-price-default.json";
char countFileLocation[64]="./pm-item-sold-count-default.json";
+269 -30
View File
@@ -2,61 +2,300 @@
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include <json-c/json.h>
#include "../include/logic.h"
#include "../include/globals.h"
/*
* TODO: Listare prodotti con prezzo da file
* TODO: Aggiungere prodotto con prezzo a file
* TODO: Listare copie vendute di prodotto da file
* TODO: Aggiungere copie venduto di prodotto a file
* TODO: Cambiare nome dei file con interagisce
* TODO: Calcolare la somma delle entrate dati i file
*/
* Formato dei file (JSON):
*
* File prodotti: [ {"name": "Maglietta S", "price": 19.90}, ... ]
* File vendite: [ {"name": "Maglietta S", "sold": 12}, ... ]
*
* Compilazione: aggiungere -ljson-c (e -lncurses) al linker.
*
* TODO: Aggiungere la possibilità di registrare gli acquisti non compiuti
* con intenzioni di acquisto.
* e.g. persona vuole acquistare maglietta S ma non c'è la taglia
*/
char *products;
/* ------------------------------------------------------------------ */
/* Utilità sui file */
/* ------------------------------------------------------------------ */
int existsFile(const char *fname) {
FILE *file;
if ((file = fopen(fname, "r")))
{
FILE *file = fopen(fname, "r");
if (file) {
fclose(file);
return 1;
}
return 0;
}
void createFile(const char *fname) {
FILE *file;
file = fopen(fname, "w");
fclose(file);
/* Crea un file JSON contenente un array vuoto. Ritorna 0 se ok, -1 se errore. */
int createFile(const char *fname) {
json_object *arr = json_object_new_array();
if (!arr)
return -1;
int res = json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY);
json_object_put(arr);
return (res == 0) ? 0 : -1;
}
char* getProducts(const char *fname) {
/* FILE *file;
/* Cambia il nome di un file di dati. Ritorna 0 se ok, -1 se errore. */
int renameDataFile(const char *oldName, const char *newName) {
return (rename(oldName, newName) == 0) ? 0 : -1;
}
if((file=fopen(fname, "r"))==NULL) {
printf("Errore nell'apertura del file");
exit(1);
/* ------------------------------------------------------------------ */
/* Funzioni interne */
/* ------------------------------------------------------------------ */
/*
* Carica l'array JSON dal file (creandolo se non esiste).
* Il chiamante deve liberare il risultato con json_object_put().
* Ritorna NULL in caso di errore o se il contenuto non è un array.
*/
static json_object *loadArray(const char *fname) {
if (!existsFile(fname) && createFile(fname) != 0)
return NULL;
/* Un file vuoto (e.g. creato a mano o dalla vecchia createFile)
* non e' JSON valido: lo trattiamo come un array vuoto. */
FILE *f = fopen(fname, "r");
if (!f)
return NULL;
int empty = (fgetc(f) == EOF);
fclose(f);
if (empty)
return json_object_new_array();
json_object *arr = json_object_from_file(fname);
if (!arr)
return NULL;
if (!json_object_is_type(arr, json_type_array)) {
json_object_put(arr);
return NULL;
}
return arr;
}
/* Cerca un elemento per nome. Ritorna l'oggetto (non va liberato) o NULL. */
static json_object *findByName(json_object *arr, const char *name) {
size_t n = json_object_array_length(arr);
for (size_t i = 0; i < n; i++) {
json_object *item = json_object_array_get_idx(arr, i);
json_object *jname;
if (json_object_object_get_ex(item, "name", &jname) &&
strcmp(json_object_get_string(jname), name) == 0)
return item;
}
return NULL;
}
/* ------------------------------------------------------------------ */
/* Prodotti */
/* ------------------------------------------------------------------ */
/*
* Ritorna l'array JSON dei prodotti (da liberare con json_object_put()),
* oppure NULL in caso di errore.
*/
json_object *getProducts(const char *fname) {
return loadArray(fname);
}
/* Stampa a schermo (ncurses) la lista dei prodotti con il relativo prezzo. */
void listProducts(const char *fname) {
json_object *arr = loadArray(fname);
if (!arr) {
printw("Errore nella lettura del file %s\n", fname);
refresh();
return;
}
// TODO QUESTO CODICE NON E' MIO, RICONTROLLARE FUNZIONAMENTO fread()
size_t n = json_object_array_length(arr);
if (n == 0)
printw("Nessun prodotto presente.\n");
while(!feof(file)){
n = fread(buffer, sizeof(char), sizeof(buffer), file);
for (i=0; i<n; i++)
if (buffer[i] == c)
count++;
for (size_t i = 0; i < n; i++) {
json_object *item = json_object_array_get_idx(arr, i);
json_object *jname, *jprice;
if (json_object_object_get_ex(item, "name", &jname) &&
json_object_object_get_ex(item, "price", &jprice))
printw("%zu) %-30s %8.2f\n", i + 1,
json_object_get_string(jname),
json_object_get_double(jprice));
}
fclose(file);
*/
refresh();
json_object_put(arr);
}
int getCount(){
/*
* Aggiunge un prodotto con il suo prezzo.
* Ritorna 0 se ok, -1 se errore, 1 se il prodotto esiste già.
*/
int addProduct(const char *fname, const char *name, double price) {
json_object *arr = loadArray(fname);
if (!arr)
return -1;
return 0;
if (findByName(arr, name)) {
json_object_put(arr);
return 1;
}
json_object *item = json_object_new_object();
json_object_object_add(item, "name", json_object_new_string(name));
json_object_object_add(item, "price", json_object_new_double(price));
json_object_array_add(arr, item);
int res = json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY);
json_object_put(arr);
return (res == 0) ? 0 : -1;
}
/* Ritorna il prezzo di un prodotto, oppure -1.0 se non trovato/errore. */
double getPrice(const char *fname, const char *name) {
json_object *arr = loadArray(fname);
if (!arr)
return -1.0;
double price = -1.0;
json_object *item = findByName(arr, name);
json_object *jprice;
if (item && json_object_object_get_ex(item, "price", &jprice))
price = json_object_get_double(jprice);
json_object_put(arr);
return price;
}
/* ------------------------------------------------------------------ */
/* Copie vendute */
/* ------------------------------------------------------------------ */
/* Ritorna le copie vendute di un prodotto (0 se non presente, -1 se errore). */
int getCount(const char *fname, const char *name) {
json_object *arr = loadArray(fname);
if (!arr)
return -1;
int count = 0;
json_object *item = findByName(arr, name);
json_object *jsold;
if (item && json_object_object_get_ex(item, "sold", &jsold))
count = json_object_get_int(jsold);
json_object_put(arr);
return count;
}
/* Stampa a schermo (ncurses) le copie vendute di ogni prodotto. */
void listSold(const char *fname) {
json_object *arr = loadArray(fname);
if (!arr) {
printw("Errore nella lettura del file %s\n", fname);
refresh();
return;
}
size_t n = json_object_array_length(arr);
if (n == 0)
printw("Nessuna vendita registrata.\n");
for (size_t i = 0; i < n; i++) {
json_object *item = json_object_array_get_idx(arr, i);
json_object *jname, *jsold;
if (json_object_object_get_ex(item, "name", &jname) &&
json_object_object_get_ex(item, "sold", &jsold))
printw("%zu) %-30s %5d\n", i + 1,
json_object_get_string(jname),
json_object_get_int(jsold));
}
refresh();
json_object_put(arr);
}
/*
* Aggiunge "qty" copie vendute al prodotto indicato
* (crea la voce se non esiste ancora).
* Ritorna 0 se ok, -1 se errore.
*/
int addSold(const char *fname, const char *name, int qty) {
json_object *arr = loadArray(fname);
if (!arr)
return -1;
json_object *item = findByName(arr, name);
if (item) {
json_object *jsold;
int current = 0;
if (json_object_object_get_ex(item, "sold", &jsold))
current = json_object_get_int(jsold);
json_object_object_add(item, "sold", json_object_new_int(current + qty));
} else {
item = json_object_new_object();
json_object_object_add(item, "name", json_object_new_string(name));
json_object_object_add(item, "sold", json_object_new_int(qty));
json_object_array_add(arr, item);
}
int res = json_object_to_file_ext(fname, arr, JSON_C_TO_STRING_PRETTY);
json_object_put(arr);
return (res == 0) ? 0 : -1;
}
/* ------------------------------------------------------------------ */
/* Entrate */
/* ------------------------------------------------------------------ */
/*
* Calcola le entrate totali: somma di (prezzo * copie vendute)
* per ogni prodotto. Ritorna -1.0 in caso di errore.
*/
double getTotalRevenue(const char *productsFile, const char *soldFile) {
json_object *products = loadArray(productsFile);
if (!products)
return -1.0;
json_object *sales = loadArray(soldFile);
if (!sales) {
json_object_put(products);
return -1.0;
}
double total = 0.0;
size_t n = json_object_array_length(sales);
for (size_t i = 0; i < n; i++) {
json_object *sale = json_object_array_get_idx(sales, i);
json_object *jname, *jsold, *jprice;
if (!json_object_object_get_ex(sale, "name", &jname) ||
!json_object_object_get_ex(sale, "sold", &jsold))
continue;
json_object *prod = findByName(products, json_object_get_string(jname));
if (prod && json_object_object_get_ex(prod, "price", &jprice))
total += json_object_get_double(jprice) * json_object_get_int(jsold);
}
json_object_put(products);
json_object_put(sales);
return total;
}
+10 -4
View File
@@ -19,8 +19,9 @@
#include <string.h>
#include <ncurses.h>
#include <windows.h>
#include <logic.h>
#include "../include/windowsDeclaration.h"
#include "../include/windowsBack.h"
#include "../include/logic.h"
void printLogoBG();
@@ -49,20 +50,25 @@ int main(){
int running = 0;
while(running != 6){
printLogoBG();
refresh();
running = runMainMenu();
switch(running) {
case 1:
warning("Questa e' una stringa molto lunga . . . Ma anche un test, che ti devo dire, ci proviamo a fare cose che non sono mai state fatte, oltretutto che divertimento ci sarebbe senza esperienze nuove?");
runSellWindow();
break;
case 2:
runViewSalesWindow();
break;
case 3:
runViewProductsWindow();
break;
case 4:
runAddProductWindow();
break;
case 5:
runOptionsMenu();
runAboutWindow();
// runOptionsMenu();
break;
case 6:
break;
+73 -7
View File
@@ -6,12 +6,78 @@
#include "../include/logic.h"
#include "../include/globals.h"
int main() {
/* Working
printf("%d", existsFile("qwestoeuntest"));
createFile("qwestoeuntest");
printf("%d", existsFile("qwestoeuntest"));
*/
#define PRODUCTS_FILE "test_products.fpm"
#define SOLD_FILE "test_sold.fpm"
#define RENAMED_FILE "test_renamed.fpm"
return 0;
static int passed = 0;
static int failed = 0;
#define CHECK(cond, desc) do { \
if (cond) { passed++; printf(" [ OK ] %s\n", desc); } \
else { failed++; printf(" [FAIL] %s (riga %d)\n", desc, __LINE__); } \
} while (0)
static int near(double a, double b) {
return (a - b) < 1e-9 && (b - a) < 1e-9;
}
static void cleanup(void) {
remove(PRODUCTS_FILE);
remove(SOLD_FILE);
remove(RENAMED_FILE);
}
int main(void) {
cleanup();
printf("== File ==\n");
CHECK(existsFile(PRODUCTS_FILE) == 0, "il file non esiste all'inizio");
CHECK(createFile(PRODUCTS_FILE) == 0, "createFile ritorna 0");
CHECK(existsFile(PRODUCTS_FILE) == 1, "il file esiste dopo createFile");
/* File completamente vuoto (non JSON): deve essere trattato come array vuoto */
FILE *f = fopen(SOLD_FILE, "w");
if (f) fclose(f);
CHECK(getCount(SOLD_FILE, "Qualsiasi") == 0, "file vuoto trattato come lista vuota");
printf("== Prodotti ==\n");
CHECK(addProduct(PRODUCTS_FILE, "Maglietta S", 20.00) == 0, "aggiunta Maglietta S");
CHECK(addProduct(PRODUCTS_FILE, "Pantaloni", 35.50) == 0, "aggiunta Pantaloni");
CHECK(addProduct(PRODUCTS_FILE, "Maglietta S", 99.99) == 1, "duplicato rifiutato (ritorna 1)");
CHECK(near(getPrice(PRODUCTS_FILE, "Maglietta S"), 20.00), "prezzo Maglietta S = 20.00");
CHECK(near(getPrice(PRODUCTS_FILE, "Pantaloni"), 35.50), "prezzo Pantaloni = 35.50");
CHECK(near(getPrice(PRODUCTS_FILE, "Inesistente"), -1.0), "prodotto inesistente -> -1.0");
json_object *arr = getProducts(PRODUCTS_FILE);
CHECK(arr != NULL && json_object_array_length(arr) == 2, "getProducts ritorna 2 prodotti");
if (arr) json_object_put(arr);
printf("== Copie vendute ==\n");
CHECK(getCount(SOLD_FILE, "Maglietta S") == 0, "nessuna vendita all'inizio");
CHECK(addSold(SOLD_FILE, "Maglietta S", 2) == 0, "addSold Maglietta S +2");
CHECK(addSold(SOLD_FILE, "Maglietta S", 1) == 0, "addSold Maglietta S +1");
CHECK(addSold(SOLD_FILE, "Pantaloni", 2) == 0, "addSold Pantaloni +2");
CHECK(getCount(SOLD_FILE, "Maglietta S") == 3, "Maglietta S: 3 copie");
CHECK(getCount(SOLD_FILE, "Pantaloni") == 2, "Pantaloni: 2 copie");
CHECK(getCount(SOLD_FILE, "Inesistente") == 0, "prodotto mai venduto: 0 copie");
printf("== Entrate ==\n");
/* 3 * 20.00 + 2 * 35.50 = 131.00 */
CHECK(near(getTotalRevenue(PRODUCTS_FILE, SOLD_FILE), 131.00), "entrate totali = 131.00");
/* Una vendita di un prodotto non presente nel listino non deve contare */
addSold(SOLD_FILE, "Fantasma", 10);
CHECK(near(getTotalRevenue(PRODUCTS_FILE, SOLD_FILE), 131.00), "vendite senza prezzo ignorate");
printf("== Rinomina ==\n");
CHECK(renameDataFile(PRODUCTS_FILE, RENAMED_FILE) == 0, "renameDataFile ritorna 0");
CHECK(existsFile(PRODUCTS_FILE) == 0 && existsFile(RENAMED_FILE) == 1, "vecchio nome sparito, nuovo presente");
CHECK(near(getPrice(RENAMED_FILE, "Pantaloni"), 35.50), "i dati sopravvivono alla rinomina");
CHECK(renameDataFile("non_esiste.fpm", "altro.fpm") == -1, "rinomina di file inesistente fallisce");
cleanup();
printf("\nRisultato: %d ok, %d falliti\n", passed, failed);
return failed == 0 ? 0 : 1;
}
-276
View File
@@ -1,276 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include "../include/windows.h"
#include "../include/logic.h"
#include "../include/globals.h"
#define MAX_LEN 64
char *mainMenu[] = {
"Segna vendita",
"Vedi vendite",
"Vedi prodotti",
"Gestisci prodotti",
"Opzioni",
"Exit"
};
int n_mainMenu = sizeof(mainMenu) / sizeof(char *);
int runMainMenu(){
WINDOW *mainWin;
int startx, starty, width, height;
height = LINES / 2;
width = COLS / 2;
starty = 0;
startx = 0;
mainWin = create_newwin(height, width, starty, startx);
int highlight = 1;
int choice = 0;
int c;
keypad(mainWin, TRUE);
wwrap_print(mainWin, 1, 2, "Usa le FRECCETTE ed INVIO per scegliere un'opzione,\nin alternativa ci sono i movement di vim e SPAZIO.");
refresh();
print_menu(mainWin, height / 2 - 3, width / 2 - 9, highlight);
while(1)
{ c = wgetch(mainWin);
switch(c)
{ case KEY_UP:
case KEY_LEFT:
case 'h':
case 'k':
if(highlight == 1)
highlight = n_mainMenu;
else
--highlight;
break;
case KEY_DOWN:
case KEY_RIGHT:
case 'l':
case 'j':
if(highlight == n_mainMenu)
highlight = 1;
else
++highlight;
break;
case 10:
case 32:
choice = highlight;
break;
default:
statusf("Il tasto '%c' non e' un tasto valido", c);
refresh();
break;
}
print_menu(mainWin, height / 2 - 3, width / 2 - 9, highlight);
if(choice != 0) /* User did a choice come out of the infinite loop */
break;
}
statusf("Avvio opzione '%s'", mainMenu[choice - 1]);
clrtoeol();
refresh();
destroy_win(mainWin);
return choice;
}
void runOptionsMenu(){
WINDOW *optionsWin;
int startx, starty, width, height;
height = LINES / 2;
width = COLS / 2;
starty = 0;
startx = COLS / 2;
optionsWin = create_newwin(height, width, starty, startx);
char input[MAX_LEN + 1];
strcpy(input, productPricesFileLocation);
int ch, pos = strlen(input);
mvwprintw(optionsWin, 1, 2, "Posizione file prodotti:");
wattron(optionsWin, COLOR_PAIR(2));
for (int i = 0; i < MAX_LEN; i++) {
mvwaddch(optionsWin, 2, 2 + i, ' ');
}
mvwprintw(optionsWin, 2, 2, "%s", input);
wmove(optionsWin, 2, 2 + strlen(input));
wrefresh(optionsWin);
pos = strlen(input);
// Input nella finestra
curs_set(1);
while ((ch = wgetch(optionsWin)) != '\n') {
if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') {
if (pos > 0) {
pos--;
input[pos] = '\0';
mvwaddch(optionsWin, 2, 2 + pos, ' ');
wmove(optionsWin, 2, 2 + pos);
}
} else if (pos < MAX_LEN && ch >= 32 && ch <= 126) {
input[pos++] = ch;
input[pos] = '\0';
mvwaddch(optionsWin, 2, 2 + pos - 1, ch);
}
wrefresh(optionsWin);
}
curs_set(0);
wattroff(optionsWin, COLOR_PAIR(2));
if (exists(input)) {
statusf("Il file '%s' esiste e verra' utilizzato al posto di '%s'", input, productPricesFileLocation);
strcpy(productPricesFileLocation, input);
}
else {
warningf("Il file '%s' non esiste, utilizzo il vecchio file '%s'", input, productPricesFileLocation);
}
werase(optionsWin);
destroy_win(optionsWin);
}
void warningf(const char *fmt, ...) {
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
warning(buf);
}
void warning(char *string){
WINDOW *warningWin;
int startx, starty, width, height;
height = LINES / 2 - 2;
width = COLS / 2;
starty = LINES / 2;
startx = 0;
warningWin = create_newwin(height, width, starty, startx);
wbkgd(warningWin, COLOR_PAIR(1));
mvwprintw(warningWin, 2, 2, "ATTENZIONE!!! E' avvenuto un errore! ");
wwrap_print(warningWin, 4, 3, string);
mvwprintw(warningWin, height - 1, 3, " Premi un tasto per continuare . . . ");
wrefresh(warningWin);
getch();
werase(warningWin);
destroy_win(warningWin);
}
void statusf(const char *fmt, ...) {
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
status(buf);
}
void status(char *msg) {
attron(COLOR_PAIR(3));
mvprintw(LINES -2, 0, "%-*s", COLS, msg);
attroff(COLOR_PAIR(3));
refresh();
}
WINDOW *create_newwin(int height, int width, int starty, int startx)
{ WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, 0 , 0);
wrefresh(local_win);
return local_win;
}
void destroy_win(WINDOW *local_win)
{
wborder(local_win, ':', ':', '.', '.', '.', '.', '.', '.');
wrefresh(local_win);
delwin(local_win);
}
void print_menu(WINDOW *menu_win, int y, int x, int highlight)
{
int i;
box(menu_win, 0, 0);
for(i = 0; i < n_mainMenu; ++i)
{ if(highlight == i + 1) /* High light the present choice */
{ wattron(menu_win, A_REVERSE);
mvwprintw(menu_win, y, x, "%s", mainMenu[i]);
wattroff(menu_win, A_REVERSE);
}
else
mvwprintw(menu_win, y, x, "%s", mainMenu[i]);
++y;
}
wrefresh(menu_win);
}
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str) {
int max_width = getmaxx(win) - (start_y + 1);
int y = start_y;
int x = start_x;
int line_length = 0;
int word_length = 0;
const char *word_start = str;
while (*str) {
// Trova la prossima parola
if (*str != ' ' && *str != '\n' && *str != '\t') {
str++;
continue;
}
// Calcola lunghezza parola corrente
word_length = str - word_start;
// Gestione wrap
if (line_length + word_length > max_width) {
y++;
x = start_x;
line_length = 0;
}
// Stampa parola
for (int i = 0; i < word_length; i++) {
mvwaddch(win, y, x++, word_start[i]);
}
line_length += word_length;
// Gestione spazi
if (*str == ' ' || *str == '\t') {
if (line_length < max_width) {
mvwaddch(win, y, x++, ' ');
line_length++;
} else {
y++;
x = start_x;
line_length = 0;
}
} else if (*str == '\n') {
y++;
x = start_x;
line_length = 0;
}
word_start = ++str;
}
// Stampa ultima parola
word_length = strlen(word_start);
if (line_length + word_length > max_width) {
y++;
x = start_x;
}
mvwprintw(win, y, x, "%s", word_start);
}
+204
View File
@@ -0,0 +1,204 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include "../include/windowsBack.h"
#include "../include/logic.h"
#include "../include/globals.h"
#define MAX_LEN 64
// Scrivi in una finestra di warning
void warningf(const char *fmt, ...) {
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
warning(buf);
}
// Dichiara una finestra di warning, avviato da warningf
void warning(char *string){
WINDOW *warningWin;
int startx, starty, width, height;
height = LINES / 2 - 2;
width = COLS / 2;
starty = LINES / 2;
startx = 0;
warningWin = create_newwin(height, width, starty, startx);
wbkgd(warningWin, COLOR_PAIR(1));
mvwprintw(warningWin, 2, 2, "ATTENZIONE!!! E' avvenuto un errore! ");
wwrap_print(warningWin, 4, 3, string);
mvwprintw(warningWin, height - 1, 3, " Premi un tasto per continuare . . . ");
wrefresh(warningWin);
getch();
werase(warningWin);
destroy_win(warningWin);
}
// Scrivi nella barra di status
void statusf(const char *fmt, ...) {
char buf[256];
va_list args;
va_start(args, fmt);
vsnprintf(buf, sizeof(buf), fmt, args);
va_end(args);
status(buf);
}
// Posiziona il testo indicato in statusf nella barra di status
void status(char *msg) {
attron(COLOR_PAIR(3));
mvprintw(LINES -2, 0, "%-*s", COLS, msg);
attroff(COLOR_PAIR(3));
refresh();
}
WINDOW *create_newwin(int height, int width, int starty, int startx)
{ WINDOW *local_win;
local_win = newwin(height, width, starty, startx);
box(local_win, 0 , 0);
wrefresh(local_win);
return local_win;
}
void destroy_win(WINDOW *local_win)
{
//wborder(local_win, ':', ':', '.', '.', '.', '.', '.', '.');
wrefresh(local_win);
delwin(local_win);
}
// Scrive in una finestra facendo il wrapping a parola del testo in modo che non esca dalla finestra
void wwrap_print(WINDOW *win, int start_y, int start_x, const char *str) {
int max_width = getmaxx(win) - (start_y + 1);
int y = start_y;
int x = start_x;
int line_length = 0;
int word_length = 0;
const char *word_start = str;
while (*str) {
// Trova la prossima parola
if (*str != ' ' && *str != '\n' && *str != '\t') {
str++;
continue;
}
// Calcola lunghezza parola corrente
word_length = str - word_start;
// Gestione wrap
if (line_length + word_length > max_width) {
y++;
x = start_x;
line_length = 0;
}
// Stampa parola
for (int i = 0; i < word_length; i++) {
mvwaddch(win, y, x++, word_start[i]);
}
line_length += word_length;
// Gestione spazi
if (*str == ' ' || *str == '\t') {
if (line_length < max_width) {
mvwaddch(win, y, x++, ' ');
line_length++;
} else {
y++;
x = start_x;
line_length = 0;
}
} else if (*str == '\n') {
y++;
x = start_x;
line_length = 0;
}
word_start = ++str;
}
// Stampa ultima parola
word_length = strlen(word_start);
if (line_length + word_length > max_width) {
y++;
x = start_x;
}
mvwprintw(win, y, x, "%s", word_start);
}
// Elemento di UI; Menu a scelta multipla
int uiSelectMenu(WINDOW *win, int start_y, int start_x, char **options, int n_options) {
int highlight = 1, choice = 0, c;
keypad(win, TRUE);
while(1) {
for(int i = 0; i < n_options; ++i) {
if(highlight == i + 1) wattron(win, A_REVERSE);
mvwprintw(win, start_y + i, start_x, "%s", options[i]);
if(highlight == i + 1) wattroff(win, A_REVERSE);
}
wrefresh(win);
c = wgetch(win);
switch(c) {
case KEY_UP: case 'k': highlight = (highlight == 1) ? n_options : highlight - 1; break;
case KEY_DOWN: case 'j': highlight = (highlight == n_options) ? 1 : highlight + 1; break;
case 10: case 32: choice = highlight; break;
}
if(choice != 0) break;
}
return choice;
}
// Elemento di UI; Campo di inserimento del testo
void uiTextInput(WINDOW *win, int y, int x, char *input, int max_len) {
int ch, pos = strlen(input);
wattron(win, COLOR_PAIR(2));
curs_set(1);
for(int i = 0; i < max_len; i++) mvwaddch(win, y, x + i, ' ');
mvwprintw(win, y, x, "%s", input);
wmove(win, y, x + pos);
wrefresh(win);
while ((ch = wgetch(win)) != '\n') {
if (ch == KEY_BACKSPACE || ch == 127 || ch == '\b') {
if (pos > 0) {
pos--;
input[pos] = '\0';
mvwaddch(win, y, x + pos, ' ');
wmove(win, y, x + pos);
}
} else if (pos < max_len && ch >= 32 && ch <= 126) {
input[pos++] = ch;
input[pos] = '\0';
mvwaddch(win, y, x + pos - 1, ch);
}
wrefresh(win);
}
curs_set(0);
wattroff(win, COLOR_PAIR(2));
}
// Elemento di UI; Tasto a 2 stati (ON/OFF)
int uiToggleSwitch(WINDOW *win, int y, int x, int state) {
int c;
keypad(win, TRUE);
while(1) {
wattron(win, A_REVERSE);
mvwprintw(win, y, x, state ? "[ ON ] " : "[ OFF ]");
wattroff(win, A_REVERSE);
wrefresh(win);
c = wgetch(win);
if (c == 10 || c == 32) break;
if (c == KEY_LEFT || c == KEY_RIGHT || c == 'h' || c == 'j' || c == 'k' || c == 'l') state = !state;
}
mvwprintw(win, y, x, state ? "[ ON ] " : "[ OFF ]");
wrefresh(win);
return state;
}
+441
View File
@@ -0,0 +1,441 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ncurses.h>
#include "../include/windowsDeclaration.h"
#include "../include/windowsBack.h"
#include "../include/logic.h"
#include "../include/globals.h"
#define MAX_LEN 64
#define LINE_LEN 96
/*
*
* HELPER
*
*
*/
static void freeLines(char **lines, int n) {
for (int i = 0; i < n; i++)
free(lines[i]);
free(lines);
}
// Legge un campo stringa da un elemento JSON ("?" se manca)
static const char *jsonStr(json_object *item, const char *key) {
json_object *v = NULL;
const char *s = NULL;
if (json_object_object_get_ex(item, key, &v) && v)
s = json_object_get_string(v);
return s ? s : "?";
}
static double jsonDouble(json_object *item, const char *key) {
json_object *v = NULL;
return (json_object_object_get_ex(item, key, &v) && v) ? json_object_get_double(v) : 0.0;
}
static int jsonInt(json_object *item, const char *key) {
json_object *v = NULL;
return (json_object_object_get_ex(item, key, &v) && v) ? json_object_get_int(v) : 0;
}
// Costruisce le righe "nome prezzo" dall'array dei prodotti.
// *n contiene il numero di righe create. Ritorna NULL se malloc fallisce.
static char **buildProductLines(json_object *products, int *n) {
int count = (int)json_object_array_length(products);
char **lines = malloc((count > 0 ? count : 1) * sizeof *lines);
if (!lines) {
*n = 0;
return NULL;
}
int made = 0;
for (int i = 0; i < count; i++) {
json_object *item = json_object_array_get_idx(products, i);
lines[i] = malloc(LINE_LEN);
if (!lines[i])
break;
snprintf(lines[i], LINE_LEN, "%-26.26s %8.2f",
jsonStr(item, "name"), jsonDouble(item, "price"));
made++;
}
*n = made;
return lines;
}
/*
* Finestra a elenco scorrevole.
* - selectable = 1: ritorna l'indice scelto con INVIO/SPAZIO, -1 se annullato (q)
* - selectable = 0: sola lettura, ritorna sempre -1
* "footer" (puo' essere NULL) e' una riga di testo fissa sopra i comandi.
*/
static int listWindow(const char *title, char **lines, int n,
const char *footer, int selectable) {
int height = LINES - 4;
int width = COLS / 2;
int visible = height - 6;
int textw = width - 4;
if (visible < 1) visible = 1;
if (textw < 0) textw = 0;
WINDOW *win = create_newwin(height, width, 0, COLS / 2);
keypad(win, TRUE);
mvwprintw(win, 1, 2, "%.*s", textw, title);
int sel = 0, top = 0, result = -1, done = 0;
while (!done) {
// Mantiene la selezione dentro l'area visibile
if (sel < top) top = sel;
if (sel >= top + visible) top = sel - visible + 1;
for (int i = 0; i < visible; i++) {
mvwhline(win, 3 + i, 1, ' ', width - 2);
int idx = top + i;
if (idx >= n) continue;
if (selectable && idx == sel) wattron(win, A_REVERSE);
mvwprintw(win, 3 + i, 2, "%.*s", textw, lines[idx]);
if (selectable && idx == sel) wattroff(win, A_REVERSE);
}
if (n == 0)
mvwprintw(win, 3, 2, "Nessun elemento.");
mvwhline(win, height - 3, 1, ' ', width - 2);
if (footer)
mvwprintw(win, height - 3, 2, "%.*s", textw, footer);
mvwhline(win, height - 2, 1, ' ', width - 2);
mvwprintw(win, height - 2, 2, "%.*s", textw,
selectable ? "SU/GIU scorri INVIO scegli q annulla"
: "SU/GIU scorri q chiudi");
wrefresh(win);
switch (wgetch(win)) {
case KEY_UP: case 'k': if (sel > 0) sel--; break;
case KEY_DOWN: case 'j': if (sel < n - 1) sel++; break;
case KEY_PPAGE:
sel -= visible;
if (sel < 0) sel = 0;
break;
case KEY_NPAGE:
sel += visible;
if (sel > n - 1) sel = n - 1;
if (sel < 0) sel = 0;
break;
case 10: case 32:
if (selectable && n > 0) result = sel;
done = 1;
break;
case 'q':
done = 1;
break;
}
}
werase(win);
destroy_win(win);
return result;
}
/*
*
* Finestre
*
*
*/
void example_window() {
WINDOW *win;
int height = LINES / 2 -2;
int width = COLS / 2;
int starty = LINES / 2;
int startx = COLS / 2;
win = create_newwin(height, width, starty, startx);
mvwprintw(win, 1, 2, "Esempio Interfaccia");
char *opts[] = {"Opzione 1", "Opzione 2", "Opzione 3"};
mvwprintw(win, 3, 2, "Scegli dal menu:");
int menu_res = uiSelectMenu(win, 4, 4, opts, 3);
char txt_buf[64] = "Testo base";
mvwprintw(win, 9, 2, "Inserisci testo:");
uiTextInput(win, 9, 19, txt_buf, 30);
mvwprintw(win, 11, 2, "Attiva opzione:");
int switch_res = uiToggleSwitch(win, 11, 18, 0);
mvwprintw(win, 13, 2, "Scelto: M=%d, T=%s, S=%d", menu_res, txt_buf, switch_res);
wrefresh(win);
wgetch(win);
werase(win);
destroy_win(win);
}
int runMainMenu(){
char *mainMenu[] = {
"Segna vendita",
"Vedi vendite",
"Vedi prodotti",
"Aggiungi prodotti",
"About",
"Exit"
};
int n_mainMenu = sizeof(mainMenu) / sizeof(char *);
WINDOW *mainWin;
int height = LINES / 2;
int width = COLS / 2;
int starty = 0;
int startx = 0;
mainWin = create_newwin(height, width, starty, startx);
wwrap_print(mainWin, 1, 2, "Usa le FRECCETTE ed INVIO per scegliere un'opzione,\nin alternativa ci sono i movement di vim e SPAZIO.");
refresh();
int choice = uiSelectMenu(mainWin, height / 2 - 3, width / 2 - 9, mainMenu, n_mainMenu);
statusf("Avvio opzione '%s'", mainMenu[choice - 1]);
clrtoeol();
refresh();
destroy_win(mainWin);
return choice;
}
void runAboutWindow(){
WINDOW *aboutWin;
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!");
statusf("Uovo di pasqua ()");
wrefresh(aboutWin);
getch();
werase(aboutWin);
destroy_win(aboutWin);
}
void runAddProductWindow() {
WINDOW *addWin = create_newwin(LINES / 2, COLS / 2, 0, COLS / 2);
char name[33] = "";
char priceStr[13] = "";
char *opts[] = {"Salva", "Annulla"};
mvwprintw(addWin, 1, 2, "Aggiungi un nuovo prodotto");
mvwprintw(addWin, 3, 2, "Nome:");
uiTextInput(addWin, 4, 2, name, 32);
mvwprintw(addWin, 6, 2, "Prezzo (es. 19.90):");
uiTextInput(addWin, 7, 2, priceStr, 12);
int choice = uiSelectMenu(addWin, 9, 4, opts, 2);
if (choice == 1) {
for (char *p = priceStr; *p; p++)
if (*p == ',') *p = '.';
char *end;
double price = strtod(priceStr, &end);
if (name[0] == '\0') {
warningf("Il nome del prodotto non puo' essere vuoto.");
} else if (priceStr[0] == '\0' || *end != '\0' || price < 0) {
warningf("Il prezzo '%s' non e' valido.", priceStr);
} else {
int res = addProduct(productPricesFileLocation, name, price);
if (res == 0)
statusf("Prodotto '%s' aggiunto a %.2f", name, price);
else if (res == 1)
warningf("Il prodotto '%s' esiste gia'.", name);
else
warningf("Errore nella scrittura del file '%s'.", productPricesFileLocation);
}
} else {
statusf("Operazione annullata");
}
werase(addWin);
destroy_win(addWin);
}
void runOptionsMenu(){
WINDOW *optionsWin;
optionsWin = create_newwin(LINES/2, COLS/2, 0, COLS/2);
char input[MAX_LEN + 1];
strcpy(input, productPricesFileLocation);
mvwprintw(optionsWin, 1, 2, "Posizione file prodotti:");
uiTextInput(optionsWin, 2, 2, input, MAX_LEN);
if (existsFile(input)) {
statusf("Il file '%s' esiste e verra' utilizzato al posto di '%s'", input, productPricesFileLocation);
strcpy(productPricesFileLocation, input);
} else {
warningf("Il file '%s' non esiste, utilizzo il vecchio file '%s'", input, productPricesFileLocation);
}
werase(optionsWin);
destroy_win(optionsWin);
}
/* ------------------------------------------------------------------ */
/* Segna vendita */
/* ------------------------------------------------------------------ */
void runSellWindow() {
json_object *products = getProducts(productPricesFileLocation);
if (!products) {
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
return;
}
if (json_object_array_length(products) == 0) {
warningf("Non ci sono prodotti. Aggiungine uno da 'Gestisci prodotti'.");
json_object_put(products);
return;
}
// 1. Scelta del prodotto
int n;
char **lines = buildProductLines(products, &n);
if (!lines) {
warningf("Memoria insufficiente.");
json_object_put(products);
return;
}
int idx = listWindow("Segna vendita - scegli il prodotto", lines, n, NULL, 1);
freeLines(lines, n);
if (idx < 0) {
statusf("Operazione annullata");
json_object_put(products);
return;
}
json_object *item = json_object_array_get_idx(products, idx);
const char *name = jsonStr(item, "name");
double price = jsonDouble(item, "price");
// 2. Quantita' venduta
WINDOW *win = create_newwin(9, COLS / 2, 0, COLS / 2);
keypad(win, TRUE);
char qtyStr[8] = "1";
char *opts[] = {"Salva", "Annulla"};
mvwprintw(win, 1, 2, "Segna vendita");
mvwprintw(win, 3, 2, "Prodotto: %.30s", name);
mvwprintw(win, 4, 2, "Quantita' venduta:");
uiTextInput(win, 5, 2, qtyStr, 6);
int choice = uiSelectMenu(win, 6, 4, opts, 2);
if (choice == 1) {
char *end;
long qty = strtol(qtyStr, &end, 10);
if (qtyStr[0] == '\0' || *end != '\0' || qty <= 0 || qty > 1000000) {
warningf("La quantita' '%s' non e' valida.", qtyStr);
} else if (addSold(countFileLocation, name, (int)qty) == 0) {
statusf("Registrata vendita: %ld x '%s' = %.2f", qty, name, qty * price);
} else {
warningf("Errore nella scrittura del file '%s'.", countFileLocation);
}
} else {
statusf("Operazione annullata");
}
werase(win);
destroy_win(win);
json_object_put(products);
}
/* ------------------------------------------------------------------ */
/* Vedi vendite */
/* ------------------------------------------------------------------ */
void runViewSalesWindow() {
json_object *sales = getProducts(countFileLocation);
if (!sales) {
warningf("Impossibile leggere il file '%s'.", countFileLocation);
return;
}
int count = (int)json_object_array_length(sales);
char **lines = malloc((count > 0 ? count : 1) * sizeof *lines);
if (!lines) {
warningf("Memoria insufficiente.");
json_object_put(sales);
return;
}
int n = 0;
for (int i = 0; i < count; i++) {
json_object *item = json_object_array_get_idx(sales, i);
const char *name = jsonStr(item, "name");
int sold = jsonInt(item, "sold");
double price = getPrice(productPricesFileLocation, name);
lines[i] = malloc(LINE_LEN);
if (!lines[i])
break;
if (price >= 0)
snprintf(lines[i], LINE_LEN, "%-18.18s %4d %9.2f", name, sold, sold * price);
else
snprintf(lines[i], LINE_LEN, "%-18.18s %4d %9s", name, sold, "n/d");
n++;
}
char footer[64];
double total = getTotalRevenue(productPricesFileLocation, countFileLocation);
if (total >= 0)
snprintf(footer, sizeof footer, "Entrate totali: %.2f", total);
else
snprintf(footer, sizeof footer, "Entrate totali: n/d");
listWindow("Vendite (prodotto / copie / incasso)", lines, n, footer, 0);
freeLines(lines, n);
json_object_put(sales);
}
/* ------------------------------------------------------------------ */
/* Vedi prodotti */
/* ------------------------------------------------------------------ */
void runViewProductsWindow() {
json_object *products = getProducts(productPricesFileLocation);
if (!products) {
warningf("Impossibile leggere il file '%s'.", productPricesFileLocation);
return;
}
int n;
char **lines = buildProductLines(products, &n);
if (!lines) {
warningf("Memoria insufficiente.");
json_object_put(products);
return;
}
char footer[64];
snprintf(footer, sizeof footer, "Prodotti in listino: %d", n);
listWindow("Prodotti (nome / prezzo)", lines, n, footer, 0);
freeLines(lines, n);
json_object_put(products);
}