From 23ec5d5f324c30cd6587f1cdf48c872a92e2b53e Mon Sep 17 00:00:00 2001 From: "d.viti" Date: Mon, 13 Oct 2025 23:25:33 +0200 Subject: [PATCH] Add multi-mode HTML, Docker, Helm chart, and deploy script - Add shop-mode.html and project-mode.html for separate calculation modes - Refactor index.html as a landing page for mode selection - Add Dockerfile with optimized nginx config and healthcheck - Add .dockerignore for cleaner Docker builds - Add deploy.sh for Helm/Kubernetes deployment automation - Add helm-chart/ with values.yaml, Chart.yaml, templates, and documentation - Update README.md with full instructions, features, and CI/CD examples --- .dockerignore | 38 + Dockerfile | 67 +- README.md | 345 ++++- deploy.sh | 234 +++ helm-chart/.helmignore | 26 + helm-chart/Chart.yaml | 26 + helm-chart/README.md | 300 ++++ helm-chart/templates/NOTES.txt | 27 + helm-chart/templates/_helpers.tpl | 60 + helm-chart/templates/configmap.yaml | 53 + helm-chart/values.yaml | 187 +++ index.html | 2054 ++++++++------------------- project-mode.html | 1499 +++++++++++++++++++ shop-mode.html | 658 +++++++++ 14 files changed, 4021 insertions(+), 1553 deletions(-) create mode 100644 .dockerignore create mode 100755 deploy.sh create mode 100644 helm-chart/.helmignore create mode 100644 helm-chart/Chart.yaml create mode 100644 helm-chart/README.md create mode 100644 helm-chart/templates/NOTES.txt create mode 100644 helm-chart/templates/_helpers.tpl create mode 100644 helm-chart/templates/configmap.yaml create mode 100644 helm-chart/values.yaml create mode 100644 project-mode.html create mode 100644 shop-mode.html diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..31788e0 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,38 @@ +# Git +.git +.gitignore +.gitea + +# Helm +helm-chart/ +*.md + +# CI/CD +.gitlab-ci.yml +.github/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +Thumbs.db + +# Backup files +*.bak +*.tmp +*.orig + +# Documentation +README.md +LICENSE +docs/ + +# Node modules (if any) +node_modules/ +package-lock.json +yarn.lock diff --git a/Dockerfile b/Dockerfile index 4790254..13682c7 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,70 @@ +# Multi-stage build for optimized nginx image FROM nginx:alpine -COPY index.html /usr/share/nginx/html/ +# Remove default nginx static assets +RUN rm -rf /usr/share/nginx/html/* +# Copy static files +COPY index.html /usr/share/nginx/html/ +COPY project-mode.html /usr/share/nginx/html/ +COPY shop-mode.html /usr/share/nginx/html/ + +# Create a custom nginx configuration for better caching and security +RUN cat > /etc/nginx/conf.d/default.conf <<'EOF' +server { + listen 80; + server_name _; + root /usr/share/nginx/html; + index index.html; + + # Security headers + add_header X-Frame-Options "SAMEORIGIN" always; + add_header X-Content-Type-Options "nosniff" always; + add_header X-XSS-Protection "1; mode=block" always; + add_header Referrer-Policy "no-referrer-when-downgrade" always; + + # Gzip compression + gzip on; + gzip_vary on; + gzip_proxied any; + gzip_comp_level 6; + gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml; + + # Cache static assets + location ~* \.(jpg|jpeg|png|gif|ico|css|js|svg|woff|woff2|ttf|eot)$ { + expires 1y; + add_header Cache-Control "public, immutable"; + } + + # Main location + location / { + try_files $uri $uri/ /index.html; + } + + # Health check endpoint + location /health { + access_log off; + return 200 "healthy\n"; + add_header Content-Type text/plain; + } +} +EOF + +# Run nginx as non-root user +RUN chown -R nginx:nginx /usr/share/nginx/html && \ + chown -R nginx:nginx /var/cache/nginx && \ + chown -R nginx:nginx /var/log/nginx && \ + touch /var/run/nginx.pid && \ + chown -R nginx:nginx /var/run/nginx.pid + +USER nginx + +# Expose port 80 EXPOSE 80 -CMD ["nginx", "-g", "daemon off;"] \ No newline at end of file +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD wget --quiet --tries=1 --spider http://localhost/health || exit 1 + +# Start nginx +CMD ["nginx", "-g", "daemon off;"] diff --git a/README.md b/README.md index c1011be..ab60f81 100644 --- a/README.md +++ b/README.md @@ -1,77 +1,308 @@ # Calcolatore Prezzi Software -Un'applicazione web per calcolare i prezzi di progetti software in base a diversi parametri e metodologie di stima. +Sistema professionale per la creazione di preventivi software con due modalità operative: + +- **Menu Principale** (`index.html`): Pagina di selezione della modalità con interfaccia intuitiva +- **Modalità Progetto** (`project-mode.html`): Calcolo basato su ore, persone, milestone e regime fiscale italiano +- **Modalità Negozio** (`shop-mode.html`): Calcolo basato su articoli, quantità e listino prezzi (come un e-commerce) ## Caratteristiche -- Interfaccia utente moderna e responsive -- Calcolo automatico dei prezzi basato su parametri configurabili -- Supporto per diverse metodologie di stima -- Design ottimizzato per il mercato italiano +### Modalità Standard -## Installazione e Utilizzo +- ✅ Gestione team con tariffe personalizzate per membro +- ✅ Calcolo milestone con assegnazione task a membri specifici +- ✅ Supporto per diversi regimi fiscali italiani (Forfettario, Ordinario, SRL, SRLS, ecc.) +- ✅ Calcolo automatico di IRPEF, IRES, IRAP, INPS +- ✅ Generazione PDF per cliente e documento interno +- ✅ Salvataggio e caricamento preventivi +- ✅ Interfaccia responsive con Alpine.js e Tailwind CSS -### Utilizzo con Docker +### Modalità Negozio -1. **Costruire l'immagine Docker:** - ```bash - docker build -t git.commandware.com/dnviti/calcolatore_prezzi_software . - ``` - -2. **Eseguire il container:** - ```bash - docker run -p 8080:80 git.commandware.com/dnviti/calcolatore_prezzi_software - ``` - -3. **Oppure utilizzare l'immagine dal registry:** - ```bash - docker pull git.commandware.com/dnviti/calcolatore_prezzi_software:main - docker run -p 8080:80 git.commandware.com/dnviti/calcolatore_prezzi_software:main - ``` - -3. **Accedere all'applicazione:** - Aprire il browser e navigare a `http://localhost:8080` - -### Sviluppo Locale - -Per sviluppo locale, aprire semplicemente il file `index.html` in un browser web. - -## Deployment - -Il progetto include una pipeline CI/CD per Gitea che automaticamente: - -- Costruisce l'immagine Docker -- Pubblica sul registry Gitea (git.commandware.com) -- Si attiva su push al branch `main` o su pull request - -### Configurazione Secrets - -Per utilizzare la pipeline, configurare i seguenti secrets nel repository Gitea: - -- `GITEA_USERNAME`: Username Gitea -- `GITEA_TOKEN`: Token di accesso Gitea con permessi di scrittura al registry +- ✅ Gestione catalogo articoli con codici SKU +- ✅ Calcolo prezzi unitari e quantità +- ✅ Sconti per articolo +- ✅ Gestione spese di spedizione +- ✅ Calcolo IVA configurabile +- ✅ Generazione PDF preventivo +- ✅ Interfaccia intuitiva per vendita prodotti/servizi ## Struttura del Progetto ``` . -├── index.html # Applicazione web principale -├── Dockerfile # Configurazione Docker -├── .gitea/ -│ └── workflows/ -│ └── build.yml # Pipeline CI/CD -└── README.md # Documentazione +├── index.html # Menu principale per selezione modalità +├── project-mode.html # Modalità progetto (ore/persone/milestone) +├── shop-mode.html # Modalità negozio (articoli/quantità) +├── Dockerfile # Build immagine Docker +├── deploy.sh # Script deploy automatico +├── .dockerignore # Ignore file per Docker +└── helm-chart/ # Helm chart per Kubernetes + ├── Chart.yaml # Definizione chart + ├── values.yaml # Valori di configurazione + ├── README.md # Documentazione Helm + ├── .helmignore # Ignore file per Helm + └── templates/ # Template Kubernetes + ├── _helpers.tpl # Helper functions + ├── configmap.yaml # ConfigMap per HTML + └── NOTES.txt # Note post-install +``` + +## Quick Start + +### Utilizzo Locale + +1. **Apri direttamente nel browser:** + + ```bash + # Modalità standard + firefox index.html + + # Modalità negozio + firefox shop-mode.html + ``` + +2. **Oppure con un server web locale:** + + ```bash + # Python 3 + python3 -m http.server 8000 + + # PHP + php -S localhost:8000 + + # Node.js (con http-server) + npx http-server -p 8000 + ``` + + Poi visita: `http://localhost:8000` + +### Deploy con Docker + +1. **Build dell'immagine:** + + ```bash + docker build -t calcolatore-prezzi:latest . + ``` + +2. **Run del container:** + + ```bash + docker run -d -p 8080:80 --name calcolatore calcolatore-prezzi:latest + ``` + +3. **Accesso:** + ``` + http://localhost:8080/ # Menu principale + http://localhost:8080/project-mode.html # Modalità progetto + http://localhost:8080/shop-mode.html # Modalità negozio + ``` + +### Deploy su Kubernetes con Helm + +#### Prerequisiti + +- Kubernetes cluster (1.19+) +- Helm 3.0+ +- kubectl configurato + +#### Deploy Rapido + +1. **Aggiorna le dipendenze Helm:** + + ```bash + cd helm-chart + helm dependency update + cd .. + ``` + +2. **Deploy con lo script automatico:** + + ```bash + # Deploy base + ./deploy.sh + + # Deploy in namespace specifico + ./deploy.sh -n production + + # Deploy con valori personalizzati + ./deploy.sh -f values-prod.yaml -n production -t v1.0.0 + + # Upgrade deployment esistente + ./deploy.sh -u -n production -t v1.0.1 + ``` + +3. **Deploy manuale con Helm:** + ```bash + helm install calcolatore-prezzi ./helm-chart \ + --namespace production \ + --create-namespace \ + --set-file configMaps.html-content.data.index\.html=./index.html \ + --set-file configMaps.html-content.data.project-mode\.html=./project-mode.html \ + --set-file configMaps.html-content.data.shop-mode\.html=./shop-mode.html + ``` + +#### Configurazione Ingress + +Modifica `helm-chart/values.yaml`: + +```yaml +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + hosts: + - host: calcolatore.tuodominio.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: calcolatore-tls + hosts: + - calcolatore.tuodominio.com +``` + +#### Verifica Deploy + +```bash +# Stato dei pod +kubectl get pods -n production + +# Logs +kubectl logs -n production -l app.kubernetes.io/name=calcolatore-prezzi -f + +# Port-forward per test locale +kubectl port-forward -n production svc/calcolatore-prezzi 8080:80 +``` + +## Helm Chart + +Il chart utilizza [base-helm](https://git.commandware.com/GitOps/base-helm.git) come dipendenza comune per standardizzare le risorse Kubernetes. + +### Parametri Principali + +| Parametro | Descrizione | Default | +| ------------------------- | ------------------- | ----------- | +| `replicaCount` | Numero di repliche | `2` | +| `image.repository` | Repository immagine | `nginx` | +| `image.tag` | Tag immagine | `alpine` | +| `service.type` | Tipo service | `ClusterIP` | +| `service.port` | Porta service | `80` | +| `ingress.enabled` | Abilita ingress | `true` | +| `resources.limits.cpu` | Limite CPU | `200m` | +| `resources.limits.memory` | Limite memoria | `256Mi` | +| `autoscaling.enabled` | Abilita HPA | `false` | + +Vedi `helm-chart/README.md` per la documentazione completa. + +## CI/CD Examples + +### GitHub Actions + +```yaml +name: Deploy + +on: + push: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Build Docker image + run: docker build -t registry.example.com/calcolatore:${{ github.sha }} . + + - name: Push image + run: docker push registry.example.com/calcolatore:${{ github.sha }} + + - name: Deploy to Kubernetes + run: | + helm dependency update ./helm-chart + ./deploy.sh -u -n production -t ${{ github.sha }} +``` + +### GitLab CI + +```yaml +stages: + - build + - deploy + +build: + stage: build + script: + - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA . + - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA + +deploy: + stage: deploy + script: + - helm dependency update ./helm-chart + - ./deploy.sh -u -n production -t $CI_COMMIT_SHA + only: + - main ``` ## Tecnologie Utilizzate -- HTML5 -- CSS3 (Tailwind CSS) -- JavaScript -- Nginx (per serving statico) -- Docker -- Gitea Actions +- **Frontend:** HTML5, CSS3, JavaScript (Vanilla + Alpine.js) +- **Styling:** Tailwind CSS (via CDN) +- **PDF Generation:** jsPDF +- **Icons:** Font Awesome +- **Containerization:** Docker +- **Orchestration:** Kubernetes + Helm 3 +- **Web Server:** Nginx Alpine + +## Funzionalità Aggiuntive + +### Modalità Standard + +- Calcolo dettagliato per regime fiscale italiano +- Supporto INPS, rivalsa, ritenuta d'acconto +- Gestione team con tariffe differenziate +- Milestone con task assegnabili a membri specifici +- Due PDF separati: cliente (pubblico) e interno (riservato) + +### Modalità Negozio + +- Gestione inventario articoli +- Codici SKU personalizzabili +- Sconti per singolo articolo +- Calcolo spese di spedizione +- IVA configurabile (0%, 4%, 10%, 22%) + +## Browser Supportati + +- Chrome/Chromium 90+ +- Firefox 88+ +- Safari 14+ +- Edge 90+ ## Licenza -[Inserire informazioni sulla licenza] \ No newline at end of file +[Inserisci qui la tua licenza] + +## Autore + +[Il tuo nome] + +## Supporto + +Per problemi o domande, apri una issue nel repository. + +## Contributing + +Le pull request sono benvenute. Per modifiche importanti, apri prima una issue per discutere cosa vorresti cambiare. + +--- + +**Note:** + +- Questo progetto è pensato per il mercato italiano e include calcoli fiscali specifici +- I calcoli fiscali sono indicativi e dovresti sempre consultare un commercialista +- Il chart Helm usa base-helm come standard per deployment Kubernetes diff --git a/deploy.sh b/deploy.sh new file mode 100755 index 0000000..e5288d5 --- /dev/null +++ b/deploy.sh @@ -0,0 +1,234 @@ +#!/bin/bash + +# Deploy script for Calcolatore Prezzi Software +# This script helps deploy the application to Kubernetes using Helm + +set -e + +# Colors for output +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +# Configuration +CHART_PATH="./helm-chart" +RELEASE_NAME="calcolatore-prezzi" +NAMESPACE="default" +VALUES_FILE="" +IMAGE_TAG="latest" + +# Functions +print_info() { + echo -e "${GREEN}[INFO]${NC} $1" +} + +print_warn() { + echo -e "${YELLOW}[WARN]${NC} $1" +} + +print_error() { + echo -e "${RED}[ERROR]${NC} $1" +} + +usage() { + cat << EOF +Usage: $0 [OPTIONS] + +Deploy Calcolatore Prezzi Software to Kubernetes using Helm + +OPTIONS: + -h, --help Show this help message + -n, --namespace NAME Kubernetes namespace (default: default) + -r, --release NAME Helm release name (default: calcolatore-prezzi) + -f, --values FILE Values file for Helm + -t, --tag TAG Docker image tag (default: latest) + -u, --upgrade Upgrade existing release + -d, --dry-run Perform a dry run + --uninstall Uninstall the release + --build Build Docker image before deploy + +EXAMPLES: + # Basic deployment + $0 + + # Deploy with custom values + $0 -f values-production.yaml -n production + + # Upgrade existing deployment + $0 -u -t v1.2.3 -n production + + # Dry run + $0 -d -f values-production.yaml + + # Build and deploy + $0 --build -t v1.0.0 + + # Uninstall + $0 --uninstall -n production +EOF +} + +check_requirements() { + print_info "Checking requirements..." + + if ! command -v helm &> /dev/null; then + print_error "Helm is not installed. Please install Helm 3.0+" + exit 1 + fi + + if ! command -v kubectl &> /dev/null; then + print_error "kubectl is not installed. Please install kubectl" + exit 1 + fi + + print_info "All requirements satisfied" +} + +update_dependencies() { + print_info "Updating Helm dependencies..." + cd "$CHART_PATH" + helm dependency update + cd - > /dev/null + print_info "Dependencies updated" +} + +build_image() { + print_info "Building Docker image..." + docker build -t "calcolatore-prezzi:${IMAGE_TAG}" . + print_info "Docker image built successfully: calcolatore-prezzi:${IMAGE_TAG}" +} + +deploy() { + local action="install" + local dry_run="" + + if [ "$UPGRADE" = true ]; then + action="upgrade" + fi + + if [ "$DRY_RUN" = true ]; then + dry_run="--dry-run --debug" + fi + + print_info "Preparing to ${action} release '${RELEASE_NAME}' in namespace '${NAMESPACE}'..." + + # Build helm command + local helm_cmd="helm ${action} ${RELEASE_NAME} ${CHART_PATH}" + helm_cmd="${helm_cmd} --namespace ${NAMESPACE}" + helm_cmd="${helm_cmd} --create-namespace" + + if [ -n "$VALUES_FILE" ]; then + helm_cmd="${helm_cmd} -f ${VALUES_FILE}" + fi + + helm_cmd="${helm_cmd} --set image.tag=${IMAGE_TAG}" + + # Inject HTML files + helm_cmd="${helm_cmd} --set-file configMaps.html-content.data.index\\.html=./index.html" + helm_cmd="${helm_cmd} --set-file configMaps.html-content.data.project-mode\\.html=./project-mode.html" + helm_cmd="${helm_cmd} --set-file configMaps.html-content.data.shop-mode\\.html=./shop-mode.html" + + if [ -n "$dry_run" ]; then + helm_cmd="${helm_cmd} ${dry_run}" + fi + + print_info "Executing: ${helm_cmd}" + eval "$helm_cmd" + + if [ "$DRY_RUN" != true ]; then + print_info "Deployment successful!" + print_info "Checking deployment status..." + kubectl rollout status deployment/${RELEASE_NAME} -n ${NAMESPACE} --timeout=5m || true + fi +} + +uninstall() { + print_warn "Uninstalling release '${RELEASE_NAME}' from namespace '${NAMESPACE}'..." + helm uninstall ${RELEASE_NAME} -n ${NAMESPACE} + print_info "Release uninstalled successfully" +} + +# Parse arguments +UPGRADE=false +DRY_RUN=false +UNINSTALL=false +BUILD=false + +while [[ $# -gt 0 ]]; do + case $1 in + -h|--help) + usage + exit 0 + ;; + -n|--namespace) + NAMESPACE="$2" + shift 2 + ;; + -r|--release) + RELEASE_NAME="$2" + shift 2 + ;; + -f|--values) + VALUES_FILE="$2" + shift 2 + ;; + -t|--tag) + IMAGE_TAG="$2" + shift 2 + ;; + -u|--upgrade) + UPGRADE=true + shift + ;; + -d|--dry-run) + DRY_RUN=true + shift + ;; + --uninstall) + UNINSTALL=true + shift + ;; + --build) + BUILD=true + shift + ;; + *) + print_error "Unknown option: $1" + usage + exit 1 + ;; + esac +done + +# Main execution +print_info "===== Calcolatore Prezzi Software - Deploy Script =====" + +check_requirements + +if [ "$UNINSTALL" = true ]; then + uninstall + exit 0 +fi + +if [ "$BUILD" = true ]; then + build_image +fi + +update_dependencies +deploy + +print_info "===== Deployment Complete =====" + +if [ "$DRY_RUN" != true ]; then + print_info "" + print_info "To check the status:" + print_info " kubectl get pods -n ${NAMESPACE} -l app.kubernetes.io/name=${RELEASE_NAME}" + print_info "" + print_info "To view logs:" + print_info " kubectl logs -n ${NAMESPACE} -l app.kubernetes.io/name=${RELEASE_NAME} -f" + print_info "" + print_info "To access the application:" + print_info " kubectl port-forward -n ${NAMESPACE} svc/${RELEASE_NAME} 8080:80" + print_info " Then visit: http://localhost:8080" +fi diff --git a/helm-chart/.helmignore b/helm-chart/.helmignore new file mode 100644 index 0000000..cd389c1 --- /dev/null +++ b/helm-chart/.helmignore @@ -0,0 +1,26 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ +# Custom +README.md +.helmignore diff --git a/helm-chart/Chart.yaml b/helm-chart/Chart.yaml new file mode 100644 index 0000000..c1471f1 --- /dev/null +++ b/helm-chart/Chart.yaml @@ -0,0 +1,26 @@ +apiVersion: v2 +name: calcolatore-prezzi-software +description: A Helm chart for Calcolatore Prezzi Software application +type: application +version: 1.0.0 +appVersion: "1.0.0" + +# Dependency on base-helm common chart +dependencies: + - name: base-helm + version: "*" + repository: "https://git.commandware.com/GitOps/base-helm.git" + alias: common + +maintainers: + - name: Your Name + email: your-email@example.com + +keywords: + - calculator + - pricing + - software + - static-website + +sources: + - https://github.com/your-repo/calcolatore-prezzi-software diff --git a/helm-chart/README.md b/helm-chart/README.md new file mode 100644 index 0000000..a836f3f --- /dev/null +++ b/helm-chart/README.md @@ -0,0 +1,300 @@ +# Calcolatore Prezzi Software - Helm Chart + +Questo è un Helm chart semplificato per il deploy del Calcolatore Prezzi Software che utilizza il [base-helm](https://git.commandware.com/GitOps/base-helm.git) come chart comune. + +## Prerequisiti + +- Kubernetes 1.19+ +- Helm 3.0+ +- Nginx Ingress Controller (opzionale, per l'ingress) +- Cert-manager (opzionale, per i certificati SSL) + +## Installazione + +### 1. Aggiornare le dipendenze + +Prima di installare il chart, è necessario aggiornare le dipendenze per scaricare il base-helm: + +```bash +cd helm-chart +helm dependency update +``` + +### 2. Installazione base + +```bash +helm install calcolatore-prezzi ./helm-chart +``` + +### 3. Installazione con valori personalizzati + +```bash +helm install calcolatore-prezzi ./helm-chart -f custom-values.yaml +``` + +### 4. Installazione con file HTML personalizzati + +Per iniettare i tuoi file HTML durante l'installazione: + +```bash +helm install calcolatore-prezzi ./helm-chart \ + --set-file configMaps.html-content.data.index\.html=./index.html \ + --set-file configMaps.html-content.data.shop-mode\.html=./shop-mode.html +``` + +### 5. Installazione in un namespace specifico + +```bash +kubectl create namespace calcolatore +helm install calcolatore-prezzi ./helm-chart -n calcolatore +``` + +## Configurazione + +I seguenti parametri possono essere configurati nel file `values.yaml`: + +### Parametri Applicazione + +| Parametro | Descrizione | Default | +|-----------|-------------|---------| +| `replicaCount` | Numero di repliche del pod | `2` | +| `image.repository` | Repository dell'immagine Docker | `nginx` | +| `image.tag` | Tag dell'immagine Docker | `alpine` | +| `image.pullPolicy` | Policy di pull dell'immagine | `IfNotPresent` | + +### Parametri Service + +| Parametro | Descrizione | Default | +|-----------|-------------|---------| +| `service.type` | Tipo di service Kubernetes | `ClusterIP` | +| `service.port` | Porta del service | `80` | +| `service.targetPort` | Porta target del container | `80` | + +### Parametri Ingress + +| Parametro | Descrizione | Default | +|-----------|-------------|---------| +| `ingress.enabled` | Abilita l'ingress | `true` | +| `ingress.className` | Classe dell'ingress controller | `nginx` | +| `ingress.hosts[0].host` | Hostname per l'ingress | `calcolatore.example.com` | +| `ingress.tls[0].secretName` | Nome del secret TLS | `calcolatore-tls` | + +### Parametri Risorse + +| Parametro | Descrizione | Default | +|-----------|-------------|---------| +| `resources.limits.cpu` | Limite CPU | `200m` | +| `resources.limits.memory` | Limite memoria | `256Mi` | +| `resources.requests.cpu` | Request CPU | `100m` | +| `resources.requests.memory` | Request memoria | `128Mi` | + +### Parametri Autoscaling + +| Parametro | Descrizione | Default | +|-----------|-------------|---------| +| `autoscaling.enabled` | Abilita l'HPA | `false` | +| `autoscaling.minReplicas` | Numero minimo di repliche | `2` | +| `autoscaling.maxReplicas` | Numero massimo di repliche | `5` | +| `autoscaling.targetCPUUtilizationPercentage` | Target CPU per scaling | `80` | + +## Esempi di Configurazione + +### values-production.yaml + +```yaml +replicaCount: 3 + +image: + repository: your-registry.io/calcolatore-prezzi + tag: "1.0.0" + pullPolicy: Always + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/rate-limit: "100" + hosts: + - host: calcolatore.yourdomain.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: calcolatore-prod-tls + hosts: + - calcolatore.yourdomain.com + +resources: + limits: + cpu: 500m + memory: 512Mi + requests: + cpu: 250m + memory: 256Mi + +autoscaling: + enabled: true + minReplicas: 3 + maxReplicas: 10 + targetCPUUtilizationPercentage: 70 + +podDisruptionBudget: + enabled: true + minAvailable: 2 +``` + +### values-development.yaml + +```yaml +replicaCount: 1 + +image: + repository: nginx + tag: "alpine" + pullPolicy: IfNotPresent + +ingress: + enabled: true + className: "nginx" + hosts: + - host: calcolatore.dev.local + paths: + - path: / + pathType: Prefix + tls: [] + +resources: + limits: + cpu: 100m + memory: 128Mi + requests: + cpu: 50m + memory: 64Mi + +autoscaling: + enabled: false +``` + +## Aggiornamento + +Per aggiornare il deployment: + +```bash +helm upgrade calcolatore-prezzi ./helm-chart +``` + +Con file HTML aggiornati: + +```bash +helm upgrade calcolatore-prezzi ./helm-chart \ + --set-file configMaps.html-content.data.index\.html=./index.html \ + --set-file configMaps.html-content.data.shop-mode\.html=./shop-mode.html +``` + +## Disinstallazione + +```bash +helm uninstall calcolatore-prezzi +``` + +## Testing + +Per testare il chart senza installarlo: + +```bash +# Dry run +helm install calcolatore-prezzi ./helm-chart --dry-run --debug + +# Template rendering +helm template calcolatore-prezzi ./helm-chart + +# Lint +helm lint ./helm-chart +``` + +## Deploy con CI/CD + +### GitLab CI Example + +```yaml +deploy: + stage: deploy + image: alpine/helm:latest + script: + - helm dependency update ./helm-chart + - helm upgrade --install calcolatore-prezzi ./helm-chart + --namespace production + --create-namespace + --set image.tag=$CI_COMMIT_SHA + --set-file configMaps.html-content.data.index\.html=./index.html + --set-file configMaps.html-content.data.shop-mode\.html=./shop-mode.html + only: + - main +``` + +### GitHub Actions Example + +```yaml +name: Deploy to Kubernetes + +on: + push: + branches: [ main ] + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + + - name: Install Helm + uses: azure/setup-helm@v1 + with: + version: '3.9.0' + + - name: Deploy + run: | + helm dependency update ./helm-chart + helm upgrade --install calcolatore-prezzi ./helm-chart \ + --namespace production \ + --create-namespace \ + --set-file configMaps.html-content.data.index\.html=./index.html \ + --set-file configMaps.html-content.data.shop-mode\.html=./shop-mode.html +``` + +## Troubleshooting + +### Verificare lo stato del deployment + +```bash +kubectl get pods -l app.kubernetes.io/name=calcolatore-prezzi +kubectl describe pod +kubectl logs +``` + +### Verificare la configurazione + +```bash +helm get values calcolatore-prezzi +helm get manifest calcolatore-prezzi +``` + +### Verificare l'ingress + +```bash +kubectl get ingress +kubectl describe ingress calcolatore-prezzi +``` + +## Note + +- Questo chart usa il base-helm come dipendenza per standardizzare le risorse Kubernetes +- I file HTML vengono iniettati tramite ConfigMap +- Per ambienti di produzione, considera l'uso di un registry Docker privato e immagini custom +- Assicurati di configurare correttamente i certificati SSL per la produzione + +## Supporto + +Per problemi o domande, apri una issue nel repository del progetto. diff --git a/helm-chart/templates/NOTES.txt b/helm-chart/templates/NOTES.txt new file mode 100644 index 0000000..a0a5415 --- /dev/null +++ b/helm-chart/templates/NOTES.txt @@ -0,0 +1,27 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "common.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "common.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "common.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "common.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} + +2. The Calcolatore Prezzi Software application has been deployed! + - Main menu: /index.html + - Project mode: /project-mode.html + - Shop mode: /shop-mode.html diff --git a/helm-chart/templates/_helpers.tpl b/helm-chart/templates/_helpers.tpl new file mode 100644 index 0000000..b0b781e --- /dev/null +++ b/helm-chart/templates/_helpers.tpl @@ -0,0 +1,60 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "calcolatore.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +*/}} +{{- define "calcolatore.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "calcolatore.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "calcolatore.labels" -}} +helm.sh/chart: {{ include "calcolatore.chart" . }} +{{ include "calcolatore.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "calcolatore.selectorLabels" -}} +app.kubernetes.io/name: {{ include "calcolatore.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "calcolatore.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "calcolatore.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/helm-chart/templates/configmap.yaml b/helm-chart/templates/configmap.yaml new file mode 100644 index 0000000..a846161 --- /dev/null +++ b/helm-chart/templates/configmap.yaml @@ -0,0 +1,53 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "calcolatore.fullname" . }}-html-content + labels: + {{- include "calcolatore.labels" . | nindent 4 }} +data: + # Note: In a real deployment, you would use a CI/CD pipeline to inject these files + # or mount them from a separate volume. This is just a simple example. + # You can also use --set-file flag with helm to inject files: + # helm install myapp ./helm-chart \ + # --set-file configMaps.html-content.data.index\.html=./index.html \ + # --set-file configMaps.html-content.data.project-mode\.html=./project-mode.html \ + # --set-file configMaps.html-content.data.shop-mode\.html=./shop-mode.html + + index.html: | + + + + + Calcolatore Prezzi - Selezione Modalità + + + +

Loading...

+ + + + project-mode.html: | + + + + + Calcolatore Prezzi - Modalità Progetto + + + +

Loading...

+ + + + shop-mode.html: | + + + + + Calcolatore Prezzi - Modalità Negozio + + + +

Loading...

+ + diff --git a/helm-chart/values.yaml b/helm-chart/values.yaml new file mode 100644 index 0000000..10e4d63 --- /dev/null +++ b/helm-chart/values.yaml @@ -0,0 +1,187 @@ +# Default values for calcolatore-prezzi-software +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +# Use base-helm common chart for standard configurations +common: + # Name override + nameOverride: "" + fullnameOverride: "calcolatore-prezzi" + +# Application configuration +replicaCount: 2 + +image: + repository: nginx + pullPolicy: IfNotPresent + tag: "alpine" + +imagePullSecrets: [] + +serviceAccount: + create: true + annotations: {} + name: "" + +podAnnotations: {} + +podSecurityContext: + fsGroup: 2000 + runAsNonRoot: true + runAsUser: 1000 + +securityContext: + capabilities: + drop: + - ALL + readOnlyRootFilesystem: true + allowPrivilegeEscalation: false + +service: + type: ClusterIP + port: 80 + targetPort: 80 + annotations: {} + +ingress: + enabled: true + className: "nginx" + annotations: + cert-manager.io/cluster-issuer: "letsencrypt-prod" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + hosts: + - host: calcolatore.example.com + paths: + - path: / + pathType: Prefix + tls: + - secretName: calcolatore-tls + hosts: + - calcolatore.example.com + +resources: + limits: + cpu: 200m + memory: 256Mi + requests: + cpu: 100m + memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 2 + maxReplicas: 5 + targetCPUUtilizationPercentage: 80 + targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: + podAntiAffinity: + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + podAffinityTerm: + labelSelector: + matchExpressions: + - key: app.kubernetes.io/name + operator: In + values: + - calcolatore-prezzi + topologyKey: kubernetes.io/hostname + +# Liveness and Readiness probes +livenessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + successThreshold: 1 + failureThreshold: 3 + +readinessProbe: + httpGet: + path: / + port: 80 + initialDelaySeconds: 5 + periodSeconds: 5 + timeoutSeconds: 3 + successThreshold: 1 + failureThreshold: 3 + +# Volume mounts for static files +volumeMounts: + - name: html-content + mountPath: /usr/share/nginx/html + readOnly: true + - name: nginx-cache + mountPath: /var/cache/nginx + - name: nginx-run + mountPath: /var/run + +volumes: + - name: html-content + configMap: + name: calcolatore-html-content + - name: nginx-cache + emptyDir: {} + - name: nginx-run + emptyDir: {} + +# ConfigMap for HTML files +configMaps: + html-content: + data: + # The HTML files will be injected here + # In production, you would use a CI/CD pipeline to update these + index.html: | + + shop-mode.html: | + + +# Environment variables +env: [] + # - name: ENVIRONMENT + # value: "production" + +# Additional labels +labels: {} + +# Pod Disruption Budget +podDisruptionBudget: + enabled: true + minAvailable: 1 + +# Network Policy +networkPolicy: + enabled: false + policyTypes: + - Ingress + - Egress + ingress: + - from: + - namespaceSelector: + matchLabels: + name: ingress-nginx + ports: + - protocol: TCP + port: 80 + egress: + - to: + - namespaceSelector: {} + ports: + - protocol: TCP + port: 53 + - protocol: UDP + port: 53 + +# Monitoring +monitoring: + enabled: false + serviceMonitor: + enabled: false + interval: 30s + path: /metrics diff --git a/index.html b/index.html index 9590da8..d0dd6d7 100644 --- a/index.html +++ b/index.html @@ -1,1499 +1,565 @@ - + - - - - Calcolatore Prezzi Software Pro - Italia - - - - - - - - - - - - - - - - - - - -
- - -
-

- - Calcolatore Prezzi Software Pro -

-

Sistema professionale per preventivi sviluppo software in Italia

-
- -
-
- - -
-
- - -
-
-
- - -
-

- - Dati Azienda -

-
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-

- - Dati Cliente -

-
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
-
- - -
-

- - Membri del Team - INTERNO -

- - -
- -
- - - - -
-

- - Team attivo: -

-

- Tariffa media sviluppo: | - Tariffa media supporto: -

-
-
- - -
- -
- - - -
- - -
- -
-

- - Configurazione Regime Fiscale -

-
-
- - -
- - - - - - - - - - - -
-
- - -
-
-
-
- - -
-

- - Tariffe Orarie -

-
-
- - -
-
- - -
-
- - -
-

- - Con le tariffe attuali, una giornata di 8 ore vale: -

-
-
-

Sviluppo

-

-
-
-

Supporto

-

-
-
-
-
- - -
-

- - Gestione Milestone Progetto -

- - -
-

- - Prima Milestone (MVP) -

-
-
- - -
-
- - -
-
- - -
-
-
-
-

- Costo MVP: - -

-
-
- - -
-
-
- - -
- -
- - -
-
-
- - -
-

- - Riepilogo Preventivo e Analisi -

- -
- -
-

- - Preventivo Cliente - CLIENTE -

-
-
- - -
-
- Rivalsa INPS (4%) - -
-
- IVA (22%) - -
-
- Ritenuta d'acconto (20%) - -
-
-
- - -
-
-
-
- - -
-

- - Analisi Interna - INTERNO -

-
-
- - -
-
- Commissione Rete (10%) - -
-
- Contributi INPS (26.07%) - -
-
- - -
-
-
- Reddito Netto Stimato - -
-
-
-
-
- - -
-
- -

Margine

-

-
-
- -

Ore Totali

-

-
-
- -

Ore Sviluppo

-

-
-
- -

Ore Supporto

-

-
-
-
- - -
-
- - - - - -
-
- - -
-
-

- - Carica Preventivo Salvato -

-
- -
-
- -

Nessun preventivo salvato

-
-
- -
-
-
-
- - - - \ No newline at end of file + + @keyframes float { + 0%, + 100% { + transform: translateY(0px); + } + 50% { + transform: translateY(-20px); + } + } + + @keyframes gradient { + 0% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } + 100% { + background-position: 0% 50%; + } + } + + .animate-fade-in { + animation: fadeIn 0.8s ease-out; + } + + .animate-float { + animation: float 3s ease-in-out infinite; + } + + .gradient-animate { + background: linear-gradient( + -45deg, + #667eea, + #764ba2, + #f093fb, + #4facfe + ); + background-size: 400% 400%; + animation: gradient 15s ease infinite; + } + + /* Effetto glass morphism */ + .glass { + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37); + } + + .glass-white { + background: rgba(255, 255, 255, 0.95); + backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.3); + } + + /* Card hover effects */ + .mode-card { + transition: all 0.4s cubic-bezier(0.175, 0.885, 0.32, 1.275); + } + + .mode-card:hover { + transform: translateY(-10px) scale(1.02); + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3); + } + + .mode-card::before { + content: ""; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 1.5rem; + padding: 2px; + background: linear-gradient( + 45deg, + transparent, + rgba(255, 255, 255, 0.3), + transparent + ); + -webkit-mask: + linear-gradient(#fff 0 0) content-box, + linear-gradient(#fff 0 0); + -webkit-mask-composite: xor; + mask-composite: exclude; + opacity: 0; + transition: opacity 0.4s ease; + } + + .mode-card:hover::before { + opacity: 1; + } + + /* Custom scrollbar */ + ::-webkit-scrollbar { + width: 10px; + } + + ::-webkit-scrollbar-track { + background: rgba(255, 255, 255, 0.1); + } + + ::-webkit-scrollbar-thumb { + background: linear-gradient(180deg, #667eea 0%, #764ba2 100%); + border-radius: 10px; + } + + + +
+ +
+
+
+ +
+
+

+ Calcolatore Prezzi +

+

+ Sistema Professionale per Preventivi +

+

+ Scegli la modalità di calcolo più adatta alle tue esigenze +

+
+ + + + + +
+

+ + Confronto Modalità +

+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Caratteristica + + + Progetto + + + Negozio +
+ Logica di calcolo + + Ore × Tariffe + Milestone + + Articoli × Quantità +
+ Gestione team + + + + +
+ Codici SKU + + + + +
+ Calcolo fiscale completo + + + + Solo IVA +
+ PDF generati + + 2 (Cliente + Interno) + + 1 (Preventivo) +
+ Ideale per + + Progetti software + + Vendita prodotti +
+
+
+ + +
+
+
+ + Salvataggio automatico +
+
+ + Generazione PDF +
+
+ + Responsive design +
+
+ + 100% locale - Privacy garantita +
+
+

+ Tutti i dati sono salvati solo sul tuo browser. Nessuna + informazione viene inviata a server esterni. +

+
+
+ + diff --git a/project-mode.html b/project-mode.html new file mode 100644 index 0000000..9590da8 --- /dev/null +++ b/project-mode.html @@ -0,0 +1,1499 @@ + + + + + + Calcolatore Prezzi Software Pro - Italia + + + + + + + + + + + + + + + + + + + +
+ + +
+

+ + Calcolatore Prezzi Software Pro +

+

Sistema professionale per preventivi sviluppo software in Italia

+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

+ + Dati Azienda +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

+ + Dati Cliente +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

+ + Membri del Team + INTERNO +

+ + +
+ +
+ + + + +
+

+ + Team attivo: +

+

+ Tariffa media sviluppo: | + Tariffa media supporto: +

+
+
+ + +
+ +
+ + + +
+ + +
+ +
+

+ + Configurazione Regime Fiscale +

+
+
+ + +
+ + + + + + + + + + + +
+
+ + +
+
+
+
+ + +
+

+ + Tariffe Orarie +

+
+
+ + +
+
+ + +
+
+ + +
+

+ + Con le tariffe attuali, una giornata di 8 ore vale: +

+
+
+

Sviluppo

+

+
+
+

Supporto

+

+
+
+
+
+ + +
+

+ + Gestione Milestone Progetto +

+ + +
+

+ + Prima Milestone (MVP) +

+
+
+ + +
+
+ + +
+
+ + +
+
+
+
+

+ Costo MVP: + +

+
+
+ + +
+
+
+ + +
+ +
+ + +
+
+
+ + +
+

+ + Riepilogo Preventivo e Analisi +

+ +
+ +
+

+ + Preventivo Cliente + CLIENTE +

+
+
+ + +
+
+ Rivalsa INPS (4%) + +
+
+ IVA (22%) + +
+
+ Ritenuta d'acconto (20%) + +
+
+
+ + +
+
+
+
+ + +
+

+ + Analisi Interna + INTERNO +

+
+
+ + +
+
+ Commissione Rete (10%) + +
+
+ Contributi INPS (26.07%) + +
+
+ + +
+
+
+ Reddito Netto Stimato + +
+
+
+
+
+ + +
+
+ +

Margine

+

+
+
+ +

Ore Totali

+

+
+
+ +

Ore Sviluppo

+

+
+
+ +

Ore Supporto

+

+
+
+
+ + +
+
+ + + + + +
+
+ + +
+
+

+ + Carica Preventivo Salvato +

+
+ +
+
+ +

Nessun preventivo salvato

+
+
+ +
+
+
+
+ + + + \ No newline at end of file diff --git a/shop-mode.html b/shop-mode.html new file mode 100644 index 0000000..9dd3414 --- /dev/null +++ b/shop-mode.html @@ -0,0 +1,658 @@ + + + + + + Calcolatore Prezzi - Modalità Negozio + + + + + + + + + + + + + + + + + + + +
+ + +
+

+ + Calcolatore Prezzi - Modalità Negozio +

+

Sistema di preventivi basato su articoli e quantità

+
+ + +
+
+ + +
+
+ + +
+

+ + Dati Venditore +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

+ + Dati Cliente +

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+

+ + Catalogo Articoli +

+ +
+ +
+ + +
+ + +
+

+ + Configurazione Fattura +

+ +
+
+ + +
+ +
+ + +
+ +
+ + +
+
+
+ + +
+

+ + Riepilogo Preventivo +

+ +
+
+
+ Subtotale Articoli + +
+
+ Sconto Totale + +
+
+ Spese di Spedizione + +
+
+ Imponibile + +
+
+ + +
+
+
+ TOTALE + +
+
+
+
+ + +
+
+ +

Articoli

+

+
+
+ +

Quantità Totale

+

+
+
+ +

Sconto Medio

+

+
+
+
+ + +
+
+ + + + + +
+
+
+ + + +