extracted objects

This commit is contained in:
2025-12-17 13:02:12 +01:00
commit 7dd4ea08e1
195 changed files with 70591 additions and 0 deletions

141
.gitignore vendored Normal file
View File

@@ -0,0 +1,141 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional stylelint cache
.stylelintcache
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variable files
.env
.env.*
!.env.example
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
.output
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# vuepress v2.x temp and cache directory
.temp
.cache
# Sveltekit cache directory
.svelte-kit/
# vitepress build output
**/.vitepress/dist
# vitepress cache directory
**/.vitepress/cache
# Docusaurus cache and generated files
.docusaurus
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# Firebase cache directory
.firebase/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v3
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*
.vite/

View File

@@ -0,0 +1,336 @@
# Apollinare Catering & Banqueting - Application Overview
## Descrizione Applicazione Attuale
### Cos'è Apollinare
**Apollinare Catering & Banqueting Management Software** è un gestionale completo per aziende di catering e banqueting che gestisce l'intero ciclo di vita di un evento, dalla prima richiesta del cliente fino all'esecuzione finale.
L'applicazione è attualmente in uso presso Apollinare Catering (Italia) ed è stata sviluppata su piattaforma Oracle APEX 21.1.0.
### Funzionalità Principali
#### 1. Gestione Eventi
Il cuore dell'applicazione è la gestione degli eventi di catering:
- **Creazione Evento**: Wizard guidato per la creazione di nuovi eventi
- **Dati Evento**: Data, orario cerimonia, orario evento, location, cliente
- **Tipologie**: Matrimoni, battesimi, comunioni, cresime, eventi aziendali, feste private
- **Tipo Pasto**: Pranzo, cena, pranzo buffet, cena buffet
#### 2. Workflow Stati Evento
L'evento attraversa diverse fasi:
```
PREVENTIVO (100) → Cliente interessato, preventivo in preparazione
SCHEDA (200) → Degustazione effettuata, scheda evento in preparazione
CONFERMATA (300) → Prima caparra ricevuta
QUASI CONFERMATO (350) → In attesa conferma definitiva
CONFERMATO (400) → Evento confermato, in esecuzione
SUPERATO (900) → Evento concluso o annullato
```
#### 3. Gestione Ospiti
Sistema sofisticato per la gestione degli ospiti:
- **Tipi Ospiti**: Adulti, bambini, staff, fornitori esterni
- **Conteggi Separati**: Seduti vs buffet, adulti vs bambini
- **Coefficienti**: Ogni tipo ospite ha coefficienti per il calcolo quantità
#### 4. Lista Prelievo (Pick List)
Gestione automatizzata del materiale necessario:
- **Articoli**: Catalogo completo con immagini, quantità standard, coefficienti
- **Categorie**: Posate, piatti, bicchieri, tovagliato, decorazioni, attrezzature cucina
- **Calcolo Automatico**: Le quantità vengono calcolate automaticamente in base a:
- Numero ospiti per tipo
- Coefficienti categoria (A=Adulti, S=Seduti, B=Buffet)
- Quantità standard articolo
- **Disponibilità**: Verifica impegni articoli su altri eventi nella stessa data
#### 5. Gestione Risorse (Staff)
Pianificazione del personale:
- **Tipi Risorsa**: Camerieri, cuochi, barman, responsabili sala
- **Assegnazione**: Assegnazione risorse per evento
- **Report**: Riepilogo impegni risorse per data
#### 6. Sistema Acconti e Pagamenti
Gestione finanziaria completa:
- **Caparre Automatiche**: Sistema 30% - 50% - 20%
- **Tracking Pagamenti**: Monitoraggio stato pagamenti
- **Solleciti**: Identificazione eventi con pagamenti in scadenza (65 giorni)
- **Email Automatiche**: Notifiche automatiche per pagamenti
#### 7. Reporting
Sistema di reportistica integrato:
- **Scheda Evento**: PDF completo per cliente
- **Preventivo**: Documento commerciale
- **Riepilogo Cucina**: Per lo staff di cucina
- **Riepilogo Allestimenti**: Per team setup
- **Griglia Eventi**: Vista calendario operativa
- **Report Costi**: Analisi costi per evento/categoria
#### 8. Calendario
Vista calendario interattiva:
- **Visualizzazione**: Eventi per giorno/settimana/mese
- **Colori Stati**: Codifica colore per stato evento
- **Limiti**: Controllo numero massimo eventi per data
- **Conflitti**: Verifica location già impegnate
#### 9. Gestione Degustazioni
Per eventi come matrimoni:
- **Pianificazione**: Data e dettagli degustazione
- **Tracking**: Stato degustazione
- **Note**: Preferenze e allergie
#### 10. Template Eventi
Sistema di template per velocizzare la creazione:
- **Template Predefiniti**: Configurazioni standard per tipologie evento
- **Duplicazione**: Copia evento esistente come base
- **Versionamento**: Sistema di versioni per tracciare modifiche
---
## Proposta SaaS: CaterPro
### Vision
Trasformare Apollinare in **CaterPro**, una piattaforma SaaS multi-tenant per la gestione di aziende di catering e banqueting, mantenendo le funzionalità core ma aggiungendo caratteristiche enterprise.
### Target Market
1. **Piccole Aziende di Catering** (1-10 dipendenti)
- Piano Basic
- Gestione eventi semplificata
- Fino a 50 eventi/mese
2. **Medie Aziende di Catering** (10-50 dipendenti)
- Piano Professional
- Multi-location
- Fino a 200 eventi/mese
3. **Grandi Aziende / Catene** (50+ dipendenti)
- Piano Enterprise
- Multi-brand, multi-country
- Eventi illimitati
### Architettura SaaS
```
┌─────────────────────────────────────────────────────────────────┐
│ CaterPro Cloud │
├─────────────────────────────────────────────────────────────────┤
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Tenant A │ │ Tenant B │ │ Tenant C │ ... │
│ │ (Catering │ │ (Wedding │ │ (Corporate │ │
│ │ Roma) │ │ Planner) │ │ Events) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Shared Services │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Auth/IAM │ │ Billing │ │Analytics │ │ API │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
├─────────────────────────────────────────────────────────────────┤
│ Infrastructure │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ .NET 8 │ │ React │ │PostgreSQL│ │ Azure │ │
│ │ API │ │ SPA │ │ /Oracle │ │ Cloud │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
└─────────────────────────────────────────────────────────────────┘
```
### Funzionalità SaaS Aggiuntive
#### Multi-Tenancy
- **Isolamento Dati**: Ogni cliente ha i propri dati completamente isolati
- **Customizzazione**: Logo, colori, branding personalizzabile
- **Subdomain**: cliente.caterpro.com
#### Gestione Utenti Avanzata
- **Ruoli Predefiniti**: Admin, Manager, Operatore, Cucina, Solo Lettura
- **Ruoli Custom**: Creazione ruoli personalizzati
- **SSO**: Integrazione Azure AD, Google Workspace
- **2FA**: Autenticazione a due fattori
#### Integrazioni
- **Calendario**: Google Calendar, Outlook, Apple Calendar
- **Pagamenti**: Stripe, PayPal, bonifici SEPA
- **Contabilità**: Export per Fatture in Cloud, QuickBooks, Xero
- **CRM**: Salesforce, HubSpot
- **E-commerce**: Preventivi online, pagamenti online
#### Mobile App
- **App iOS/Android**: Per staff in mobilità
- **Check-in Ospiti**: QR code per eventi
- **Inventario Mobile**: Scansione barcode articoli
- **Foto Evento**: Upload diretto da app
#### Analytics & BI
- **Dashboard Real-time**: KPI principali
- **Report Avanzati**: Analisi trend, stagionalità
- **Forecasting**: Previsioni ricavi
- **Export**: Excel, PDF, API
#### Automazioni
- **Email Marketing**: Campagne automatiche
- **Reminder**: Notifiche scadenze, follow-up
- **Workflow**: Automazione processi custom
- **Webhooks**: Integrazione con sistemi esterni
### Pricing Model
#### Basic - €49/mese
- 1 utente admin + 2 operatori
- 50 eventi/mese
- 500 articoli catalogo
- Report base
- Email support
#### Professional - €149/mese
- 5 utenti inclusi (+€15/utente aggiuntivo)
- 200 eventi/mese
- Articoli illimitati
- Multi-location (fino a 3)
- Report avanzati
- Integrazioni base
- Chat support
#### Enterprise - €399/mese
- Utenti illimitati
- Eventi illimitati
- Location illimitate
- API access
- Integrazioni premium
- White-label option
- SLA garantito
- Account manager dedicato
#### Add-ons
- **Mobile App**: +€29/mese
- **E-commerce Module**: +€49/mese
- **Advanced Analytics**: +€39/mese
- **Custom Integrations**: Su richiesta
### Stack Tecnologico Proposto
#### Backend (.NET 8)
```
├── CaterPro.API # Web API REST
├── CaterPro.Core # Domain models, interfaces
├── CaterPro.Application # Business logic, CQRS
├── CaterPro.Infrastructure # Data access, external services
├── CaterPro.Identity # Authentication/Authorization
└── CaterPro.Workers # Background jobs
```
#### Frontend (React TypeScript)
```
├── src/
│ ├── components/ # Reusable UI components
│ ├── features/ # Feature-based modules
│ │ ├── events/ # Event management
│ │ ├── inventory/ # Article/inventory
│ │ ├── calendar/ # Calendar views
│ │ ├── reports/ # Reporting
│ │ └── settings/ # Configuration
│ ├── hooks/ # Custom React hooks
│ ├── services/ # API services
│ ├── store/ # Redux/Zustand state
│ └── utils/ # Utilities
```
#### Database
- **Primary**: PostgreSQL (per SaaS cost-efficiency)
- **Alternative**: Oracle (per clienti enterprise on-premise)
- **Cache**: Redis
- **Search**: Elasticsearch (per ricerca articoli/eventi)
#### Infrastructure
- **Cloud**: Azure / AWS
- **Container**: Docker + Kubernetes
- **CI/CD**: GitHub Actions / Azure DevOps
- **Monitoring**: Application Insights / DataDog
### Roadmap Migrazione
#### Fase 1: Core Migration (3-4 mesi)
- [ ] Setup architettura .NET 8
- [ ] Migrazione modelli dati
- [ ] API REST per entità principali
- [ ] Frontend React base
- [ ] Autenticazione JWT
#### Fase 2: Feature Parity (2-3 mesi)
- [ ] Gestione eventi completa
- [ ] Sistema calcolo quantità
- [ ] Workflow stati
- [ ] Report PDF
- [ ] Calendario
#### Fase 3: SaaS Features (2-3 mesi)
- [ ] Multi-tenancy
- [ ] Billing integration
- [ ] User management avanzato
- [ ] Customization engine
#### Fase 4: Advanced Features (3-4 mesi)
- [ ] Mobile app
- [ ] Integrazioni terze parti
- [ ] Analytics avanzati
- [ ] E-commerce module
### Vantaggi Competitivi
1. **Esperienza Reale**: Basato su software in produzione da anni
2. **Specifico per Settore**: Non un gestionale generico adattato
3. **Calcolo Automatico**: Algoritmo quantità unico nel settore
4. **Workflow Collaudato**: Processo testato su centinaia di eventi
5. **Localizzazione**: Già disponibile in italiano, facilmente estendibile
### Competitor Analysis
| Feature | CaterPro | Caterease | Total Party Planner | Better Cater |
|---------|----------|-----------|---------------------|--------------|
| Gestione Eventi | ✅ | ✅ | ✅ | ✅ |
| Calcolo Auto Quantità | ✅ | ❌ | ❌ | Parziale |
| Multi-tenant | ✅ | ❌ | ❌ | ✅ |
| Mobile App | ✅ | ✅ | ❌ | ✅ |
| Italiano | ✅ | ❌ | ❌ | ❌ |
| API Pubbliche | ✅ | Parziale | ❌ | ✅ |
| Prezzo Entry | €49 | $75 | $50 | $99 |
---
## Conclusioni
L'applicazione Apollinare rappresenta un'eccellente base per lo sviluppo di una soluzione SaaS nel settore catering. Le funzionalità core sono mature e testate, la business logic è ben documentata, e l'architettura può essere modernizzata mantenendo la compatibilità con i processi esistenti.
La migrazione a .NET + React TypeScript permetterà:
- Scalabilità orizzontale per SaaS
- Developer experience moderna
- Ecosystem di librerie più ampio
- Deployment cloud-native
- Costi operativi ridotti
Il mercato italiano del catering è frammentato e sottosevito da soluzioni software moderne, rappresentando un'opportunità significativa per un prodotto SaaS verticale ben eseguito.

422
docs/README.md Normal file
View File

@@ -0,0 +1,422 @@
# Apollinare Catering - Documentazione Completa
Questa documentazione contiene l'estrazione completa di tutti gli oggetti del database Oracle e dell'applicazione APEX di Apollinare Catering & Banqueting.
## [Application Overview](APPLICATION_OVERVIEW.md)
**Apollinare Catering & Banqueting Management Software** è un gestionale completo per aziende di catering che gestisce l'intero ciclo di vita di un evento: dalla richiesta del cliente, al preventivo, alla conferma, fino all'esecuzione.
### Funzionalità Principali
| Area | Descrizione |
| --------------------- | ------------------------------------------- |
| **Gestione Eventi** | Creazione, workflow stati, versioning |
| **Gestione Ospiti** | Tipologie ospiti, conteggi, coefficienti |
| **Lista Prelievo** | Calcolo automatico quantità materiale |
| **Risorse/Staff** | Pianificazione personale per evento |
| **Acconti/Pagamenti** | Sistema caparre 30%-50%-20%, solleciti |
| **Calendario** | Vista eventi, limiti giornalieri, conflitti |
| **Reporting** | Schede evento, preventivi, report cucina |
### Proposta SaaS: CaterPro
La documentazione include una proposta per trasformare Apollinare in **CaterPro**, una piattaforma SaaS multi-tenant:
- **Target**: Piccole, medie e grandi aziende di catering
- **Stack**: .NET 8 + React TypeScript + PostgreSQL/Oracle
- **Pricing**: Da €49/mese (Basic) a €399/mese (Enterprise)
- **Roadmap**: 10-14 mesi per feature parity + SaaS
Leggi la [documentazione completa](APPLICATION_OVERVIEW.md) per dettagli su architettura, funzionalità e roadmap.
---
## Struttura della Documentazione
```
docs/
├── apex/ # Applicazione APEX
│ ├── README.md # Overview applicazione
│ ├── pages/ # 56 pagine
│ ├── processes/ # 98 processi
│ ├── lovs/ # 12 List of Values
│ ├── javascript/ # Librerie JavaScript
│ ├── authorization/ # 5 schemi autorizzazione
│ ├── dynamic-actions/ # Azioni dinamiche
│ ├── items/ # Items condivisi
│ ├── regions/ # Regioni condivise
│ └── navigation/ # Navigazione
├── tables/ # 32 tabelle
├── views/ # 26 viste
├── procedures/ # 11 stored procedures
├── functions/ # 23 funzioni
├── packages/ # 17 packages
├── triggers/ # 19 triggers
├── sequences/ # 22 sequences
└── types/ # 10 tipi custom
```
---
## APEX Application Documentation
### [APEX Application Overview](apex/README.md)
**Application:** APCB Project (ID: 112)
**APEX Version:** 21.1.0
**Schema:** APOLLINARECATERINGPROD
| Component | Count |
| --------------- | ----- |
| Pages | 56 |
| Items | 302 |
| Processes | 98 |
| Regions | 151 |
| Buttons | 119 |
| Dynamic Actions | 62 |
| LOVs | 12 |
### Key APEX Documentation
- [APEX README](apex/README.md) - Application overview and navigation
- [Processes Documentation](apex/processes/README.md) - All 98 processes with PL/SQL code
- [LOVs Documentation](apex/lovs/README.md) - 12 List of Values definitions
- [JavaScript Libraries](apex/javascript/README.md) - Custom ajaxUtils.js and iframeObj.js
- [Authorization Schemes](apex/authorization/README.md) - 5 security schemes
### Critical APEX Pages
| Page | Name | Description |
| ------ | ---------------- | ------------------------------------------------- |
| 1 | Home | Dashboard principale |
| **22** | **Nuovo Evento** | **Pagina più complessa (108 items, 32 processi)** |
| 9 | Liste | Lista eventi |
| 12 | Calendario | Calendario eventi |
| 35 | Schede | Schede evento |
---
## Indice per Categoria
### [Tabelle](tables/README.md) (32)
Tabelle principali del dominio business:
- [EVENTI](tables/EVENTI.md) - Tabella principale eventi
- [EVENTI_DET_PREL](tables/EVENTI_DET_PREL.md) - Liste prelievo
- [EVENTI_DET_OSPITI](tables/EVENTI_DET_OSPITI.md) - Dettaglio ospiti
- [EVENTI_DET_RIS](tables/EVENTI_DET_RIS.md) - Risorse assegnate
- [EVENTI_DET_DEGUST](tables/EVENTI_DET_DEGUST.md) - Degustazioni
- [EVENTI_ACCONTI](tables/EVENTI_ACCONTI.md) - Gestione acconti/pagamenti
- [EVENTI_ALTRICOSTI](tables/EVENTI_ALTRICOSTI.md) - Altri costi
- [EVENTI_ALLEG](tables/EVENTI_ALLEG.md) - Allegati
- [ARTICOLI](tables/ARTICOLI.md) - Catalogo articoli
- [COSTI_ARTICOLI](tables/COSTI_ARTICOLI.md) - Storico costi
- [CLIENTI](tables/CLIENTI.md) - Anagrafica clienti
- [LOCATION](tables/LOCATION.md) - Location eventi
- [RISORSE](tables/RISORSE.md) - Personale
Tabelle di lookup:
- [TB_TIPI_MAT](tables/TB_TIPI_MAT.md) - Tipi materiale
- [TB_CODICI_CATEG](tables/TB_CODICI_CATEG.md) - Categorie
- [TB_TIPI_EVENTO](tables/TB_TIPI_EVENTO.md) - Tipi evento
- [TB_TIPI_OSPITI](tables/TB_TIPI_OSPITI.md) - Tipi ospiti
- [TB_TIPI_RISORSA](tables/TB_TIPI_RISORSA.md) - Tipi risorsa
- [TB_TIPI_PASTO](tables/TB_TIPI_PASTO.md) - Tipi pasto
- [TB_CALENDAR_LOCKS](tables/TB_CALENDAR_LOCKS.md) - Limiti calendario
- [TB_CONFIG](tables/TB_CONFIG.md) - Configurazioni
Tabelle di sistema:
- [USERS_READONLY](tables/USERS_READONLY.md) - Permessi utenti
- [XLIB_LOGS](tables/XLIB_LOGS.md) - Log applicazione
- [XLIB_COMPONENTS](tables/XLIB_COMPONENTS.md) - Componenti
- [XLIB_JASPERREPORTS_CONF](tables/XLIB_JASPERREPORTS_CONF.md) - Config report
- [XLIB_JASPERREPORTS_DEMOS](tables/XLIB_JASPERREPORTS_DEMOS.md) - Demo report
### [Viste](views/README.md) (26)
Viste per calcolo costi:
- [GET_COSTO_ART_BY_EVT](views/GET_COSTO_ART_BY_EVT.md) - Costo articoli per evento
- [GET_COSTO_ART_EVT](views/GET_COSTO_ART_EVT.md) - Costo articoli aggregato
- [GET_COSTO_CATEG_EVT](views/GET_COSTO_CATEG_EVT.md) - Costo per categoria
- [GET_COSTO_DEGUS_EVT](views/GET_COSTO_DEGUS_EVT.md) - Costo degustazioni
- [GET_COSTO_OSPITI_EVT](views/GET_COSTO_OSPITI_EVT.md) - Costo ospiti
- [GET_COSTO_RIS_EVT](views/GET_COSTO_RIS_EVT.md) - Costo risorse
- [GET_COSTO_TIPI_EVT](views/GET_COSTO_TIPI_EVT.md) - Costo per tipo
- [GET_ULTIMI_COSTI](views/GET_ULTIMI_COSTI.md) - Ultimi costi articoli
Viste per eventi:
- [GET_EVT_DATA](views/GET_EVT_DATA.md) - Dati evento completi
- [GET_EVT_DATA_PRINT](views/GET_EVT_DATA_PRINT.md) - Dati per stampa
- [GET_PREL_ART_TOT](views/GET_PREL_ART_TOT.md) - Totali prelievo
- [GET_PREL_BY_EVT](views/GET_PREL_BY_EVT.md) - Prelievi per evento
Viste per calendario e stato:
- [VW_CALENDARIO_EVENTI](views/VW_CALENDARIO_EVENTI.md) - Vista calendario
- [VW_EVENT_COLOR](views/VW_EVENT_COLOR.md) - Colori stati
- [VW_EVENTI_STATUSES](views/VW_EVENTI_STATUSES.md) - Stati eventi
Viste per giacenze:
- [V_IMPEGNI_ARTICOLI](views/V_IMPEGNI_ARTICOLI.md) - Impegni articoli
- [V_IMPEGNI_ARTICOLI_LOC](views/V_IMPEGNI_ARTICOLI_LOC.md) - Impegni per location
Viste per report:
- [V_REP_ALLESTIMENTI](views/V_REP_ALLESTIMENTI.md) - Report allestimenti
- [VW_REP_DEGUSTAZIONI](views/VW_REP_DEGUSTAZIONI.md) - Report degustazioni
- [V_GRIGLIA](views/V_GRIGLIA.md) - Vista griglia
- [GET_REPORT_CONSUNTIVO_PER_DATA](views/GET_REPORT_CONSUNTIVO_PER_DATA.md) - Consuntivo
Viste per utenti/permessi:
- [GET_CONSUNTIVI_USERS](views/GET_CONSUNTIVI_USERS.md) - Utenti consuntivi
- [GET_GESTORI_USERS](views/GET_GESTORI_USERS.md) - Utenti gestori
- [GET_USERS_LIST](views/GET_USERS_LIST.md) - Lista utenti
Viste per pagamenti:
- [GET_EVENTI_DA_PAGARE_ENTRO_65GG](views/GET_EVENTI_DA_PAGARE_ENTRO_65GG.md) - Eventi da sollecitare
### [Stored Procedures](procedures/README.md) (11)
Business logic principale:
- [EVENTI_AGGIORNA_QTA_LISTA](procedures/EVENTI_AGGIORNA_QTA_LISTA.md) - Ricalcolo quantità lista prelievo
- [EVENTI_AGGIORNA_TOT_OSPITI](procedures/EVENTI_AGGIORNA_TOT_OSPITI.md) - Aggiorna totale ospiti
- [EVENTI_COPIA](procedures/EVENTI_COPIA.md) - Duplicazione evento
- [EVENTI_RICALCOLA_ACCONTI](procedures/EVENTI_RICALCOLA_ACCONTI.md) - Ricalcolo acconti
- [EVENTO_ELIMINA_PRELIEVI](procedures/EVENTO_ELIMINA_PRELIEVI.md) - Elimina prelievi
- [LISTE_COPIA](procedures/LISTE_COPIA.md) - Copia liste tra eventi
- [P_CANCEL_SAME_LOCATION_EVENTS](procedures/P_CANCEL_SAME_LOCATION_EVENTS.md) - Annulla eventi stessa location
Utility:
- [ROWSORT_TIPI](procedures/ROWSORT_TIPI.md) - Ordinamento tipi
- [HTPPRN](procedures/HTPPRN.md) - Stampa HTTP
- [SEND_DATA_TO_DROPBOX](procedures/SEND_DATA_TO_DROPBOX.md) - Export Dropbox
- [XLOG](procedures/XLOG.md) - Logging
### [Funzioni](functions/README.md) (23)
Calcolo quantità e disponibilità:
- [F_GET_QTA_IMPEGNATA](functions/F_GET_QTA_IMPEGNATA.md) - Quantità impegnata
- [F_GET_TOT_OSPITI](functions/F_GET_TOT_OSPITI.md) - Totale ospiti
- [F_GET_OSPITI](functions/F_GET_OSPITI.md) - Dettaglio ospiti (pipelined)
- [F_LIST_PRELIEVO_ADD_ARTICOLO](functions/F_LIST_PRELIEVO_ADD_ARTICOLO.md) - Aggiunta articolo
Calcolo costi:
- [F_GET_COSTO_ARTICOLO](functions/F_GET_COSTO_ARTICOLO.md) - Costo articolo a data
Validazioni:
- [F_EVENTO_SCADUTO](functions/F_EVENTO_SCADUTO.md) - Verifica scadenza
- [F_MAX_NUMERO_EVENTI_RAGGIUNTO](functions/F_MAX_NUMERO_EVENTI_RAGGIUNTO.md) - Limite eventi
- [F_MAX_NUM_EVENTI_CONFERMATI](functions/F_MAX_NUM_EVENTI_CONFERMATI.md) - Limite confermati
- [F_CI_SONO_EVENTI_CONFERMATI](functions/F_CI_SONO_EVENTI_CONFERMATI.md) - Check confermati
Report:
- [F_REP_ALLESTIMENTI](functions/F_REP_ALLESTIMENTI.md) - Report allestimenti
- [F_REP_CUCINA](functions/F_REP_CUCINA.md) - Report cucina
- [F_GET_ANGOLO_ALLESTIMENTO](functions/F_GET_ANGOLO_ALLESTIMENTO.md) - Angolo allestimento
- [F_GET_ANGOLO_ALLESTIMENTO_OB](functions/F_GET_ANGOLO_ALLESTIMENTO_OB.md) - Angolo open bar
- [F_GET_TOVAGLIATO_ALLESTIMENTO](functions/F_GET_TOVAGLIATO_ALLESTIMENTO.md) - Tovagliato
Autorizzazioni:
- [F_USER_IN_ROLE](functions/F_USER_IN_ROLE.md) - Verifica ruolo utente
- [F_USER_IN_ROLE_STR](functions/F_USER_IN_ROLE_STR.md) - Ruolo utente (stringa)
Utility:
- [F_DAY_TO_NAME](functions/F_DAY_TO_NAME.md) - Giorno in italiano
- [STRING_TO_TABLE_ENUM](functions/STRING_TO_TABLE_ENUM.md) - Stringa a tabella
- [GET_PARAM_VALUE](functions/GET_PARAM_VALUE.md) - Valore parametro
- [SPLIT](functions/SPLIT.md) - Split stringa
- [MY_INSTR](functions/MY_INSTR.md) - Instr custom
- [CLOB2BLOB](functions/CLOB2BLOB.md) - Conversione CLOB
- [EXTDATE_GET_ITA](functions/EXTDATE_GET_ITA.md) - Data in italiano
### [Packages](packages/README.md) (17)
Business:
- [MAIL_PKG](packages/MAIL_PKG.md) - Gestione invio email automatiche
Utility esterne:
- [UTL_BASE64](packages/UTL_BASE64.md) - Encoding Base64
JasperReports:
- [XLIB_JASPERREPORTS](packages/XLIB_JASPERREPORTS.md) - Integrazione JasperReports
- [XLIB_JASPERREPORTS_IMG](packages/XLIB_JASPERREPORTS_IMG.md) - Immagini report
HTTP/Componenti:
- [XLIB_HTTP](packages/XLIB_HTTP.md) - Chiamate HTTP
- [XLIB_COMPONENT](packages/XLIB_COMPONENT.md) - Componenti
- [XLIB_LOG](packages/XLIB_LOG.md) - Logging
JSON (libreria PLJSON):
- [PLJSON_DYN](packages/PLJSON_DYN.md)
- [PLJSON_EXT](packages/PLJSON_EXT.md)
- [PLJSON_HELPER](packages/PLJSON_HELPER.md)
- [PLJSON_ML](packages/PLJSON_ML.md)
- [PLJSON_OBJECT_CACHE](packages/PLJSON_OBJECT_CACHE.md)
- [PLJSON_PARSER](packages/PLJSON_PARSER.md)
- [PLJSON_PRINTER](packages/PLJSON_PRINTER.md)
- [PLJSON_UT](packages/PLJSON_UT.md)
- [PLJSON_UTIL_PKG](packages/PLJSON_UTIL_PKG.md)
- [PLJSON_XML](packages/PLJSON_XML.md)
### [Triggers](triggers/README.md) (19)
Generazione ID:
- [EVENTI_TRG](triggers/EVENTI_TRG.md) - ID eventi + inizializzazione
- [EVENTI_AI_TRG](triggers/EVENTI_AI_TRG.md) - Creazione ospiti default
- [EVENTI_DET_PREL_TRG](triggers/EVENTI_DET_PREL_TRG.md) - ID prelievi
- [EVENTI_DET_RIS_TRG](triggers/EVENTI_DET_RIS_TRG.md) - ID risorse
- [EVENTI_DET_DEGUST_TRG](triggers/EVENTI_DET_DEGUST_TRG.md) - ID degustazioni
- [EVENTI_ACCONTI_TRG](triggers/EVENTI_ACCONTI_TRG.md) - ID acconti
- [EVENTI_ALTRICOSTI_TRG](triggers/EVENTI_ALTRICOSTI_TRG.md) - ID altri costi
- [EVENTI_ALLEG_TRG](triggers/EVENTI_ALLEG_TRG.md) - ID allegati
- [CLIENTI_TRG](triggers/CLIENTI_TRG.md) - ID clienti
- [LOCATION_TRG](triggers/LOCATION_TRG.md) - ID location
- [RISORSE_TRG](triggers/RISORSE_TRG.md) - ID risorse
- [ARTICOLI_DET_REGOLE_TRG](triggers/ARTICOLI_DET_REGOLE_TRG.md) - ID regole articoli
- [TB_TIPI_PASTO_TRG](triggers/TB_TIPI_PASTO_TRG.md) - ID tipi pasto
Business logic:
- [EVENTI_DET_OSPITI_TRG_AI](triggers/EVENTI_DET_OSPITI_TRG_AI.md) - Aggiornamento ospiti
- [EVENTI_DET_PREL_QTA_TOT_TRG](triggers/EVENTI_DET_PREL_QTA_TOT_TRG.md) - Calcolo quantità totale
Ordinamento:
- [ADD_COD_STEP](triggers/ADD_COD_STEP.md) - Ordine tipi materiale
- [ON_DELETE_REORDER](triggers/ON_DELETE_REORDER.md) - Riordino dopo delete
Sistema:
- [BI_GL_SCHEMA_CHANGES](triggers/BI_GL_SCHEMA_CHANGES.md) - Log modifiche schema
- [XLIB_LOGS_BI_TRG](triggers/XLIB_LOGS_BI_TRG.md) - Log applicazione
### [Sequences](sequences/README.md) (22)
Tutte le sequence del database.
### [Types](types/README.md) (10)
Tipi custom:
- [T_DET_OSPITI_ROW](types/T_DET_OSPITI_ROW.md) / [T_DET_OSPITI_TAB](types/T_DET_OSPITI_TAB.md) - Tipo per F_GET_OSPITI
- [T_REP_ALLESTIMENTI_ROW](types/T_REP_ALLESTIMENTI_ROW.md) / [T_REP_ALLESTIMENTI_TAB](types/T_REP_ALLESTIMENTI_TAB.md) - Tipo per F_REP_ALLESTIMENTI
- [T_REP_CUCINA_ROW](types/T_REP_CUCINA_ROW.md) / [T_REP_CUCINA_TAB](types/T_REP_CUCINA_TAB.md) - Tipo per F_REP_CUCINA
- [STRING_LIST](types/STRING_LIST.md) - Lista stringhe
- [ENUM_TABLE_OBJECT](types/ENUM_TABLE_OBJECT.md) / [ENUM_TABLE_TYPE](types/ENUM_TABLE_TYPE.md) - Tipi per STRING_TO_TABLE_ENUM
- [XLIB_VC2_ARRAY_T](types/XLIB_VC2_ARRAY_T.md) - Array varchar2
---
## Schema ER Semplificato
```
┌─────────────┐
│ CLIENTI │
└──────┬──────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ LOCATION │◄────│ EVENTI │────►│TB_TIPI_EVENTO│
└─────────────┘ └──────┬──────┘ └─────────────┘
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│EVENTI_DET_OSPITI│ │ EVENTI_DET_PREL │ │ EVENTI_DET_RIS │
└─────────────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ TB_TIPI_OSPITI │ │ ARTICOLI │ │ RISORSE │
└─────────────────┘ └────────┬────────┘ └─────────────────┘
┌─────────────────┐
│ TB_CODICI_CATEG │
└────────┬────────┘
┌─────────────────┐
│ TB_TIPI_MAT │
└─────────────────┘
┌─────────────────┬─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│EVENTI_DET_DEGUST│ │ EVENTI_ACCONTI │ │EVENTI_ALTRICOSTI│
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## Workflow Stati Evento
```
┌──────────────┐
│ PREVENTIVO │ (100) - Bianco
└──────┬───────┘
│ Degustazione
┌──────────────┐
│SCHEDA EVENTO │ (200) - Celeste
│(preparazione)│
└──────┬───────┘
│ Prima caparra
┌──────────────┐
│ SCHEDA │ (300) - Giallo
│ CONFERMATA │
└──────┬───────┘
│ Quasi confermato
┌──────────────┐
│SCHEDA QUASI │ (350) - Arancio
│ CONFERMATA │
└──────┬───────┘
│ Conferma definitiva
┌──────────────┐
│ CONFERMATO │ (400) - Verde
└──────────────┘
│ Rifiuto/Scadenza
┌──────────────┐
│NON ACCETTATO/│ (900) - Viola
│ SUPERATO │
└──────────────┘
```
## Note per lo Sviluppo
1. **Packages PLJSON\_\***: Libreria esterna per parsing JSON, può essere sostituita con funzionalità native .NET
2. **Packages XLIB\_\***: Componenti per integrazione JasperReports, da valutare sostituzione con report .NET
3. **Trigger per ID**: In .NET usare Identity columns o GUID
4. **Calcolo quantità**: La logica in `EVENTI_AGGIORNA_QTA_LISTA` è critica e deve essere portata fedelmente
5. **Sistema acconti**: Le percentuali 30%-50%-20% sono hardcoded, valutare parametrizzazione

195
docs/apex/README.md Normal file
View File

@@ -0,0 +1,195 @@
# APEX Application Documentation
## Application Overview
| Property | Value |
|----------|-------|
| **Application ID** | 112 |
| **Application Name** | APCB Project |
| **Application Alias** | F_110112 |
| **APEX Version** | 21.1.0 |
| **Owner/Schema** | APOLLINARECATERINGPROD |
| **Language** | Italian (it) |
| **Date Format** | DD-MM-YYYY |
| **DateTime Format** | DD-MM-YYYY HH24:MI:SS |
| **Logo Text** | Apollinare Catering & Banqueting - Management Software |
| **Last Updated By** | MONIA |
| **Last Update** | 2025-11-24 14:06:02 |
## Application Statistics
| Component | Count |
|-----------|-------|
| **Pages** | 56 |
| **Items** | 302 |
| **Processes** | 98 |
| **Regions** | 151 |
| **Buttons** | 119 |
| **Dynamic Actions** | 62 |
| **LOVs** | 12 |
| **Authorization Schemes** | 5 |
| **Computations** | 2 |
| **Breadcrumb Entries** | 22 |
| **List Entries** | 2 |
| **Templates** | 64 |
| **Plug-ins** | 2 |
| **Messages** | 464 |
## Navigation Structure
### Desktop Navigation Menu
```
Home (Page 1)
├── Articoli (Page 2)
│ └── Impegni Articoli (Page 39)
├── Categorie (Page 4)
├── Tipi (Page 6)
├── Clienti (Page 17)
├── Location (Page 15)
├── Risorse (Page 31)
├── Permessi (Page 47) [Admin Only]
├── Gestione Dati (Page 45) [Admin Only]
├── Job Schedulati (Page 49)
├── Mail Inviate (Page 50)
└── Mail In Attesa (Page 51)
Eventi
├── Tipi Evento (Page 13) [Admin Only]
├── Nuovo Evento (Page 22) [Admin Only]
├── Schede/Schede Confermate (Page 35)
├── Liste (Page 9) [Admin Only]
├── Calendario Eventi (Page 12)
├── Degustazioni (Page 27)
└── Template Eventi (Page 48) [Admin Only]
Riepiloghi/Report
├── Griglia (Page 16) [Admin Only]
├── Riepilogo Cucina (Page 25)
├── Torte e Costi Extra (Page 28) [Admin Only]
├── Riepilogo Allestimenti (Page 30)
└── Riepilogo Risorse (Page 38)
```
## Page Categories
### Master Data Pages
| Page ID | Name | Description |
|---------|------|-------------|
| 2 | Articoli Rpt | Article list report |
| 3 | Articoli | Article detail form |
| 4 | Categorie Rpt | Categories list report |
| 5 | Categorie | Category detail form |
| 6 | Tipi Rpt | Types list report |
| 7 | Tipi | Type detail form |
| 17 | Clienti Rpt | Clients list report |
| 18 | Clienti | Client detail form |
| 15 | Location Rpt | Locations list report |
| 20 | Location | Location detail form |
| 31 | Risorse | Resources (staff) management |
### Event Management Pages
| Page ID | Name | Description |
|---------|------|-------------|
| 1 | Home | Dashboard |
| 8 | Nuovo Evento Wizard | Event creation wizard |
| 9 | Lista Eventi | Event list |
| 12 | Calendario | Event calendar |
| 13 | Tipi Evento Rpt | Event types list |
| 14 | Tipi Evento | Event type detail form |
| **22** | **Nuovo Evento** | **Main event form (most complex)** |
| 27 | Lista Degustazioni | Tastings list |
| 32 | Degustazione | Tasting detail |
| 35 | Schede/Schede Confermate | Event cards |
| 48 | Template Eventi | Event templates |
### Report Pages
| Page ID | Name | Description |
|---------|------|-------------|
| 16 | Griglia | Grid view |
| 25 | Riepilogo Cucina | Kitchen summary |
| 28 | Torte e Costi Extra | Cakes and extra costs |
| 30 | Riepilogo Allestimenti | Setup summary |
| 38 | Riepilogo Risorse | Resources summary |
| 39 | Impegni Articoli | Article commitments |
### Administration Pages
| Page ID | Name | Description |
|---------|------|-------------|
| 45 | Gestione Dati | Data management |
| 46 | Max Eventi | Max events configuration |
| 47 | Permessi | Permissions management |
| 49 | Job Schedulati | Scheduled jobs |
| 50 | Mail Inviate | Sent emails |
| 51 | Mail In Attesa | Pending emails |
## Page 22 (Nuovo Evento) - Most Complex Page
Page 22 is the main event management page with the highest complexity:
| Component | Count |
|-----------|-------|
| Items | 108 |
| Regions | 33 |
| Buttons | 32 |
| Processes | 32 |
| Dynamic Actions | Multiple |
### Key Features:
- Multiple Interactive Grids (master-detail)
- Guest type management with quantity recalculations
- Resource assignments
- Pick list management with coefficient-based calculations
- Event status workflow management
- Template support
- Versioning system
## Documentation Structure
```
docs/apex/
├── README.md (this file)
├── pages/ # Individual page documentation
├── processes/ # Process documentation
├── lovs/ # List of Values documentation
├── javascript/ # JavaScript libraries
├── authorization/ # Authorization schemes
├── dynamic-actions/ # Dynamic actions
├── items/ # Shared items
├── regions/ # Shared regions
└── navigation/ # Navigation components
```
## Key Files
- [LOVs Documentation](lovs/README.md) - List of Values definitions
- [Processes Documentation](processes/README.md) - PL/SQL processes
- [JavaScript Libraries](javascript/README.md) - Custom JavaScript
- [Authorization Schemes](authorization/README.md) - Security settings
- [Page 22 Documentation](pages/PAGE_022.md) - Main event form
## Event Status Workflow
```
100 (Preventivo/Quote)
↓ [Continue Event]
200 (Scheda/Preparazione)
↓ [Return to Preparazione]
300 (Confermata/Quasi)
↓ [Almost Continue Event]
350 (Quasi Confermato)
↓ [Confirm Event]
400 (Confermato/Confirmed)
↓ [Set Obsoleto]
900 (Superato/Expired)
```
## Authorization Schemes
| Scheme Name | Description |
|-------------|-------------|
| Admin_auth_schema | Full admin access |
| User Read/Write | Controlled by USERS_READONLY table |
| Consuntivi | Financial summaries access |
| Gestori | Manager-level permissions |
| Solo Admins | Highest level (admin, monia only) |

View File

@@ -0,0 +1,296 @@
# Authorization Schemes Documentation
This document describes the 5 authorization schemes used in the APEX application for access control.
## Overview
| Scheme Name | Type | Users/Condition | Error Message |
|-------------|------|-----------------|---------------|
| Admin_auth_schema | NATIVE_EXISTS | Specific user list | Utente non autorizzato |
| User Read/Write | NATIVE_FUNCTION_BODY | USERS_READONLY table | Utente abilitato in sola lettura |
| Consuntivi | NATIVE_EXISTS | GET_CONSUNTIVI_USERS view | Autorizzazione necessaria... |
| Gestori | NATIVE_EXISTS | GET_GESTORI_USERS view | Non possiedi i permessi... |
| Solo Admins | NATIVE_EXISTS | admin, monia only | Non possiedi i permessi... |
---
## Admin_auth_schema
**Type:** NATIVE_EXISTS
**Caching:** BY_USER_BY_PAGE_VIEW
**SQL Query:**
```sql
select 1 from dual
where lower(:APP_USER) in ('admin','monia','andrea','maria','sabrina','nicole','cucina','developer','elia.ballarani');
```
**Authorized Users:**
- admin
- monia
- andrea
- maria
- sabrina
- nicole
- cucina
- developer
- elia.ballarani
**Usage:** General administrative access to most management pages.
---
## User Read/Write
**Type:** NATIVE_FUNCTION_BODY
**Caching:** BY_USER_BY_PAGE_VIEW
**PL/SQL Function:**
```plsql
declare
v_readonly number;
begin
select 1
into v_readonly
from users_readonly
where username = :APP_USER
and flgwrite = 0;
return false;
exception when no_data_found then
return true;
end;
```
**Logic:**
- Checks `USERS_READONLY` table
- If user exists with `FLGWRITE = 0` → Returns FALSE (read-only mode)
- If user not found → Returns TRUE (write access)
**Usage:** Controls whether user can modify data or only view.
---
## Consuntivi
**Type:** NATIVE_EXISTS
**Caching:** BY_USER_BY_PAGE_VIEW
**SQL Query:**
```sql
select 1 from dual
where :APP_USER in (select users from get_consuntivi_users);
```
**Data Source:** `GET_CONSUNTIVI_USERS` view
**Usage:** Access to financial summary pages and cost management.
---
## Gestori
**Type:** NATIVE_EXISTS
**Caching:** BY_USER_BY_PAGE_VIEW
**SQL Query:**
```sql
select 1 from dual
where :APP_USER in (select users from get_gestori_users);
```
**Data Source:** `GET_GESTORI_USERS` view
**Usage:** Manager-level access. Applied to:
- Home page
- Nuovo Evento (Page 22)
- Liste (Page 9)
- Griglia (Page 16)
- Torte e Costi Extra (Page 28)
- Template Eventi (Page 48)
- Tipi Evento (Page 13)
---
## Solo Admins
**Type:** NATIVE_EXISTS
**Caching:** BY_USER_BY_PAGE_VIEW
**SQL Query:**
```sql
select 1 from dual
where lower(:APP_USER) in ('admin', 'monia');
```
**Authorized Users:**
- admin
- monia
**Usage:** Highest level of access. Reserved for:
- Permessi (Page 47)
- Gestione Dati (Page 45)
- Critical configuration pages
---
## Application-Level Processes
### SET_USER_READONLY
**Process Point:** Before Header (runs on every page)
**Sequence:** 1
```plsql
begin
select 1
into :APP_READ_ONLY
from users_readonly
where username = :APP_USER
and flgwrite = 0;
exception when no_data_found then
:APP_READ_ONLY := 0;
end;
```
**Sets:** `APP_READ_ONLY` application item
- Value `1` = Read-only mode
- Value `0` = Full access
This is checked in page processes with condition:
```plsql
:APP_READ_ONLY = 0
```
---
### SET_ITEM_SESSION
**Process Point:** On Demand
**Security:** MUST_NOT_BE_PUBLIC_USER
```plsql
declare
v_itemList varchar2(32000);
begin
v_itemList := APEX_UTIL.TABLE_TO_STRING(APEX_APPLICATION.G_F01);
for c in (
select
(select result from table(STRING_TO_TABLE_ENUM(a.result, 1, '='))) as item,
(select result from table(STRING_TO_TABLE_ENUM(a.result, 2, '='))) as val
from table(STRING_TO_TABLE_ENUM(v_itemList)) a
)
loop
APEX_UTIL.set_session_state(
p_name => c.item,
p_value => c.val
);
end loop;
end;
```
**Usage:** Called by `setSessionState()` JavaScript function to update APEX session state for multiple items.
---
## Application Items
### APP_READ_ONLY
**Protection Level:** Internal
**Escape on HTTP Output:** No
Stores the read-only flag for the current user session.
### APP_VERSION
Application version identifier.
---
## Authorization Hierarchy
```
┌─────────────────────────────────────────────────────────────┐
│ Solo Admins │
│ (admin, monia) │
├─────────────────────────────────────────────────────────────┤
│ Admin_auth_schema │
│ (admin, monia, andrea, maria, sabrina, nicole, │
│ cucina, developer, elia.ballarani) │
├─────────────────────────────────────────────────────────────┤
│ Gestori │
│ (from GET_GESTORI_USERS view) │
├─────────────────────────────────────────────────────────────┤
│ Consuntivi │
│ (from GET_CONSUNTIVI_USERS view) │
├─────────────────────────────────────────────────────────────┤
│ User Read/Write │
│ (based on USERS_READONLY.FLGWRITE flag) │
├─────────────────────────────────────────────────────────────┤
│ Authenticated Users │
│ (basic application access) │
└─────────────────────────────────────────────────────────────┘
```
---
## Database Objects for Authorization
### USERS_READONLY Table
| Column | Type | Description |
|--------|------|-------------|
| USERNAME | VARCHAR2 | APEX username |
| FLGWRITE | NUMBER | 0 = read-only, 1 = write |
### GET_GESTORI_USERS View
Returns list of users with manager permissions.
### GET_CONSUNTIVI_USERS View
Returns list of users with financial access.
---
## Migration Notes
When migrating to .NET + React:
1. **Authentication**
- Implement JWT or cookie-based authentication
- Consider integration with existing Oracle users or migrate to new system
2. **Authorization**
- Map authorization schemes to .NET roles/claims
- Implement policy-based authorization in .NET
3. **Role Mapping**
```csharp
public enum UserRole
{
User, // Basic authenticated user
ReadOnly, // Read-only access
Consuntivi, // Financial access
Gestori, // Manager level
Admin, // Admin_auth_schema equivalent
SuperAdmin // Solo Admins equivalent
}
```
4. **Frontend Authorization**
- Store user roles in JWT/session
- Implement route guards in React
- Conditionally render UI elements based on permissions
5. **Backend Authorization**
```csharp
[Authorize(Policy = "Gestori")]
public class EventController : Controller
{
[Authorize(Policy = "SoloAdmins")]
public IActionResult DeleteEvent(int id) { ... }
}
```

53282
docs/apex/f112.sql Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,330 @@
# JavaScript Libraries Documentation
This document describes the custom JavaScript libraries used in the APEX application.
## Static Application Files
| File Name | Description |
|-----------|-------------|
| ajaxUtils.js | AJAX utilities for dynamic updates and server communication |
| iframeObj.js | Jasper Reports integration wrapper |
---
## ajaxUtils.js
Custom AJAX utility library for managing server-side communication and UI feedback.
### Functions
#### `notifica(pText, pType = null)`
Central dynamic notification function.
**Parameters:**
- `pText` (string): Message text to display
- `pType` (string, optional): Notification type - 'alert', 'success', 'error', 'warning', 'info'
**Behavior:**
- Clears existing errors
- If `pType` is provided, shows error notification at page level
- If `pType` is null, shows success message
**Example:**
```javascript
notifica("Record saved successfully"); // Success message
notifica("Error occurred", "error"); // Error message
```
---
#### `setSessionState(elemList, pCallback = null)`
Sets APEX session state for multiple items.
**Parameters:**
- `elemList` (string): Comma-separated list of item IDs (without P prefix)
- `pCallback` (function, optional): Callback function after session state is set
**How it works:**
1. Splits the element list by comma
2. Gets values for each element using jQuery
3. Creates a dictionary of item-value pairs
4. Calls APEX process `SET_ITEM_SESSION` to update session state
5. Executes callback with data and elements dictionary
**Example:**
```javascript
setSessionState("P22_EVENT_ID,P22_CLIENTE", function(pData, elems) {
console.log("Session state updated");
});
```
---
#### `ajaxExec(pName, clickObj, pParams, pCallback, pConfirm, pLoading, pAsync, pType)`
Generic AJAX execution wrapper for APEX server processes.
**Parameters:**
- `pName` (string): APEX process name to execute
- `clickObj` (jQuery object): Button/element that triggered the action
- `pParams` (object): Parameters to pass to the process
- `pCallback` (function): Callback function on success/error
- `pConfirm` (string): Confirmation message (uses native confirm dialog)
- `pLoading` (boolean): Show loading popup
- `pAsync` (boolean): Execute asynchronously
- `pType` (string): Response data type (default: 'text')
**Features:**
- Prevents page unload during execution
- Shows loading spinner on clicked element
- Handles confirmation dialogs
- Parses response and removes quotes
- Executes callback with parsed data
**Example:**
```javascript
ajaxExec("SAVE_EVENT", clickBtn, {x01: eventId}, function(data, btn, params) {
notifica("Event saved");
}, "Are you sure?", true);
```
---
#### `execProcessAsync(pName, pParams, pPreExec, pCallback, pItems = null, clickObj = null, pConfirm = null, pLoading = null)`
Wrapper for async process execution with optional session state update.
**Parameters:**
- `pName` (string): Process name
- `pParams` (object): Process parameters
- `pPreExec` (function): Function to execute before AJAX call
- `pCallback` (function): Callback after process execution
- `pItems` (string): Items to set in session state first
- `clickObj`: Clicked element
- `pConfirm` (string): Confirmation message
- `pLoading` (boolean): Show loading indicator
**Example:**
```javascript
execProcessAsync("UPDATE_QTY", {x01: itemId}, null, function(data) {
apex.region("LIST_PREL").refresh();
}, "P22_EVENT_ID");
```
---
#### `execQueryAsync(pQuery, pPreExec, pCallback, clickObj = null, pConfirm = null, pItems = null, pLoading = null)`
Executes a SQL query asynchronously.
**Parameters:**
- `pQuery` (string): SQL query to execute
- `pPreExec` (function): Pre-execution function
- `pCallback` (function): Callback with query results
- `clickObj`: Clicked element
- `pConfirm` (string): Confirmation message
- `pItems` (string): Items to set in session state
- `pLoading` (boolean): Show loading indicator
**Note:** Calls the `EXEC_QUERY` APEX process with the query in `x01` parameter.
---
## iframeObj.js
JavaScript wrapper for embedding Jasper Reports in iframes.
### Configuration
```javascript
var j_datasource = "default"; // Jasper datasource name
var j_username = "jasperadmin"; // Jasper server username
var j_password = "jasperadmin"; // Jasper server password
var j_def_outp = "pdf"; // Default output format (HTML, PDF)
```
### Iframe Class
#### Constructor
```javascript
var Iframe = function(parentObj, id, attr) { ... }
```
**Parameters:**
- `parentObj`: Parent DOM element
- `id`: Iframe element ID
- `attr`: Additional attributes
### Methods
#### `setCss(css)`
Sets CSS styles on the iframe.
**Parameters:**
- `css` (object): CSS properties dictionary
**Example:**
```javascript
iframe.setCss({ width: "100%", height: "600px", border: "none" });
```
---
#### `setUrl(Url)`
Sets the iframe source URL.
---
#### `getUrl()`
Returns the current iframe URL.
---
#### `refresh()`
Reloads the iframe content.
---
#### `hide()`
Hides the iframe (display: none).
---
#### `show()`
Shows the iframe (display: block).
---
#### `jasperReport(dir, datasource, params, output, username, password)`
Generates a Jasper Reports URL and loads it in the iframe.
**Parameters:**
- `dir` (string, required): Report path in Jasper server
- `datasource` (string): Data source name (default: "default")
- `params` (object): Report parameters as key-value pairs
- `output` (string): Output format - 'pdf' or 'html' (default: 'pdf')
- `username` (string): Jasper server username
- `password` (string): Jasper server password
**URL Format:**
```
/jri/report?_repName={dir}&_repFormat={output}&_dataSource={datasource}&_repLocale=it_IT&_repTimeZone=Europe/Rome&{params}
```
**Example:**
```javascript
var rptFrame = new Iframe(container, "reportFrame");
rptFrame.jasperReport(
"apcb/scheda_evento_rpt",
"default",
{ P_EVENT_ID: eventId, P_TIPO: "PREVENTIVO" },
"pdf"
);
```
---
## Usage in APEX Pages
### Time Input Masks
The application uses inputmask library for time fields:
```javascript
Inputmask("99:99", {
placeholder: "HH:MM"
}).mask($("#P22_ORA_INI_EVENTO"));
```
### SweetAlert2 Integration
Used for enhanced confirmation dialogs:
```javascript
Swal.fire({
title: 'Conferma',
text: 'Vuoi continuare?',
icon: 'warning',
showCancelButton: true
}).then((result) => {
if (result.isConfirmed) {
// proceed
}
});
```
### Interactive Grid Refresh
```javascript
// Refresh a specific interactive grid
apex.region("LISTA_PRELIEVO").refresh();
// Refresh with callback
apex.region("OSPITI").widget().interactiveGrid("getViews", "grid").model.fetchRecords();
```
---
## Migration Notes
When migrating to React TypeScript:
1. **AJAX Utilities** - Replace with:
- Axios or fetch for HTTP requests
- React Query or SWR for data fetching
- Toast library (react-toastify) for notifications
2. **Session State** - Replace with:
- React Context or Redux for state management
- URL parameters for shareable state
- Session storage for persistence
3. **Jasper Reports** - Options:
- Keep Jasper and embed via iframe
- Migrate to SSRS, Crystal Reports
- Use PDF libraries (QuestPDF, iTextSharp)
- Use React-PDF for client-side PDF generation
4. **Input Masks** - Use:
- react-input-mask
- @react-input/mask
- Material-UI TextField with InputProps
5. **SweetAlert2** - Replace with:
- MUI Dialog components
- react-confirm-alert
- Custom modal component
### Example React Migration
```typescript
// ajaxUtils.js equivalent in React
import { toast } from 'react-toastify';
import axios from 'axios';
export const notifica = (text: string, type?: 'success' | 'error' | 'warning' | 'info') => {
if (type) {
toast[type](text);
} else {
toast.success(text);
}
};
export const executeProcess = async <T>(
processName: string,
params: Record<string, any>
): Promise<T> => {
const response = await axios.post(`/api/processes/${processName}`, params);
return response.data;
};
```

242
docs/apex/lovs/README.md Normal file
View File

@@ -0,0 +1,242 @@
# List of Values (LOVs) Documentation
This document contains all 12 List of Values defined in the APEX application.
## Overview
| LOV Name | Source Type | Description |
|----------|-------------|-------------|
| ARTICOLI | SQL Query | Article selection |
| CATEGORIE | SQL Query | Category selection |
| CLIENTI | SQL Query | Client selection |
| LOCATION | SQL Query | Location selection |
| RISORSE | SQL Query | Resource (staff) selection |
| STATO_EVENTO | Static | Event status values |
| TIPI MAT | SQL Query | Material type selection |
| TIPI RISORSE | SQL Query | Resource type selection |
| TIPO_EVENTO | SQL Query | Event type selection |
| TIPO_OSPITI | SQL Query | Guest type selection |
| TIPO_PASTO | Static | Meal type values |
| USERS | SQL Query | User selection |
---
## ARTICOLI
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select /*a.cod_categ || ' - ' || c.descrizione || ' - ' ||*/ a.DESCRIZIONE as descrizione, a.COD_ARTICOLO
from articoli a
join tb_codici_categ c on a.cod_categ = c.cod_categ
order by 1
```
**Usage:** Selecting articles in pick lists and event details.
---
## CATEGORIE
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
SELECT descrizione as d,
cod_categ as r
FROM tb_codici_categ
ORDER BY 1
```
**Usage:** Filtering articles by category, category selection in forms.
---
## CLIENTI
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select CLIENTE, ID
from clienti
```
**Usage:** Client selection in event forms.
---
## LOCATION
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select location || ' - ' || indirizzo, id
from location
```
**Usage:** Location selection in event forms. Displays location name with address.
---
## RISORSE
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select NOME || ' ' || COGNOME d, id r
from risorse
```
**Usage:** Resource (staff) assignment in events. Displays full name.
---
## STATO_EVENTO
**Source Type:** Static
**Values:**
| Display Value | Return Value | Sequence |
|---------------|--------------|----------|
| Scheda Evento (Preparazione) | 0 | 10 |
| Preventivo | 10 | 20 |
| Confermato | 20 | 30 |
**Note:** The actual status values in the database have been expanded:
- 100 = Preventivo
- 200 = Scheda
- 300 = Confermata
- 350 = Quasi Confermato
- 400 = Confermato
- 900 = Superato
**Usage:** Event status display and selection.
---
## TIPI MAT
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
SELECT descrizione as d,
cod_tipo as r
FROM tb_tipi_mat
ORDER BY 1
```
**Usage:** Material type filtering in pick lists. Controls the step-by-step wizard flow.
---
## TIPI RISORSE
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select descrizione, cod_tipo
from tb_tipi_risorsa
```
**Usage:** Resource type classification (camerieri, cuochi, etc.).
---
## TIPO_EVENTO
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select trim(DESCRIZIONE) || decode(tipo_pasto, 'C', ' - Cena', 'P', ' - Pranzo', null) as d,
COD_TIPO as r
from tb_tipi_evento
```
**Usage:** Event type selection. Displays description with meal type indicator (Pranzo/Cena).
---
## TIPO_OSPITI
**Source Type:** SQL Query (LEGACY_SQL)
**Query:**
```sql
select descrizione as d,
cod_tipo as r
from tb_tipi_ospiti
order by 1
```
**Usage:** Guest type selection in event guest details.
---
## TIPO_PASTO
**Source Type:** Static
**Values:**
| Display Value | Return Value | Sequence |
|---------------|--------------|----------|
| Pranzo | P | 1 |
| Cena | C | 2 |
| Pranzo Buffet | A | 3 |
| Cena Buffet | B | 4 |
**Usage:** Meal type classification for events and event types.
---
## USERS
**Source Type:** SQL Query
**Query:**
```sql
select user_name D, user_name R
from WWV_FLOW_USERS
```
**Return Column:** R
**Display Column:** D
**Usage:** User selection for permissions and assignments.
---
## Migration Notes
When migrating to React TypeScript:
1. **Static LOVs** (STATO_EVENTO, TIPO_PASTO) can be implemented as TypeScript enums or const objects
2. **SQL-based LOVs** should be converted to API endpoints
3. Consider caching strategy for frequently used LOVs (CATEGORIE, TIPI MAT, etc.)
4. TIPO_EVENTO uses Oracle `decode()` function - convert to CASE WHEN or handle in API
### Example TypeScript Implementation
```typescript
// Static LOV - TIPO_PASTO
export const TIPO_PASTO = {
PRANZO: { value: 'P', label: 'Pranzo' },
CENA: { value: 'C', label: 'Cena' },
PRANZO_BUFFET: { value: 'A', label: 'Pranzo Buffet' },
CENA_BUFFET: { value: 'B', label: 'Cena Buffet' },
} as const;
// Dynamic LOV - API endpoint
// GET /api/lovs/articoli
// GET /api/lovs/categorie
// GET /api/lovs/clienti
// etc.
```

View File

@@ -0,0 +1,5 @@
# Page 2 - Articoli Rpt
## Items (1)
- `P2_IMMAGINE`

View File

@@ -0,0 +1,25 @@
# Page 3 - Articoli
## Items (21)
- `P3_COD_ARTICOLO`
- `P3_COD_CATEG`
- `P3_COD_RELATIVO`
- `P3_COEFF`
- `P3_COEFF_A`
- `P3_COEFF_B`
- `P3_COEFF_S`
- `P3_DESCRIZIONE`
- `P3_FLG_CUCINA`
- `P3_IMMAGINE`
- `P3_IMMAGINE_DISPLAY`
- `P3_PERC_IVA`
- `P3_PERC_OSPITI`
- `P3_QTAGIAC`
- `P3_QTAIMP`
- `P3_QTA_STD_A`
- `P3_QTA_STD_B`
- `P3_QTA_STD_S`
- `P3_RANK`
- `P3_ROWID`
- `P3_TIPO_QTA`

View File

@@ -0,0 +1,13 @@
# Page 5 - Categorie
## Items (9)
- `P5_COD_CATEG`
- `P5_COD_TIPO`
- `P5_COEFF_A`
- `P5_COEFF_B`
- `P5_COEFF_S`
- `P5_DESCRIZIONE`
- `P5_ROWID`
- `P5_SHOW_PRINT`
- `P5_TIPO_RIEPILOGO`

View File

@@ -0,0 +1,7 @@
# Page 7 - Tipi
## Items (3)
- `P7_COD_TIPO`
- `P7_DESCRIZIONE`
- `P7_ROWID`

View File

@@ -0,0 +1,39 @@
# Page 8 - Creazione Evento
## Items (35)
- `P8_ALLERGIE`
- `P8_CATEG`
- `P8_CATEG_FILTER`
- `P8_CLIENTE`
- `P8_CLIENTE_DISPLAY`
- `P8_COD_TIPO`
- `P8_COD_TIPO_DISPLAY`
- `P8_COD_TIPO_FILTER`
- `P8_CONFETTATA`
- `P8_DATA`
- `P8_DATA_DISPLAY`
- `P8_DESCRIZIONE`
- `P8_DESCRIZIONE_DISPLAY`
- `P8_EVENT_ID`
- `P8_FLG_TEMPLATE`
- `P8_ID_IMAGE_SHOW`
- `P8_INDIRIZZO`
- `P8_INDIRIZZO_DISPLAY`
- `P8_LOCATION`
- `P8_LOCATION_DISPLAY`
- `P8_MAXSTEP`
- `P8_NOTE`
- `P8_NOTE_DISPLAY`
- `P8_ORA_INI_CER`
- `P8_ORA_INI_EVT`
- `P8_PERC_SEDUTE`
- `P8_STAMPA_MENU`
- `P8_STEP`
- `P8_TAVOLI_NR`
- `P8_TORTA`
- `P8_TORTA_DISPLAY`
- `P8_TOT_ADULTI`
- `P8_TOT_BABY`
- `P8_TOT_KINDER`
- `P8_TOT_OSPITI`

View File

@@ -0,0 +1,6 @@
# Page 10 - Anteprima Immagine
## Items (2)
- `P10_IMMAGINE`
- `P10_ROWID`

View File

@@ -0,0 +1,5 @@
# Page 11 - Stampa Riepilogo
## Items (1)
- `P11_EVENT_ID`

View File

@@ -0,0 +1,9 @@
# Page 14 - Tipi Evento
## Items (5)
- `P14_COD_TIPO`
- `P14_DESCRIZIONE`
- `P14_LIVELLO`
- `P14_ROWID`
- `P14_TIPO_PASTO`

View File

@@ -0,0 +1,5 @@
# Page 16 - Griglia
## Items (1)
- `P16_SETTIMANA`

View File

@@ -0,0 +1,14 @@
# Page 18 - Clienti
## Items (10)
- `P18_CLIENTE`
- `P18_COGNOME_RIF`
- `P18_INDIRIZZO`
- `P18_MAIL`
- `P18_NOME_RIF`
- `P18_PIVA`
- `P18_RAGSOC`
- `P18_ROWID`
- `P18_TEL1`
- `P18_TEL2`

View File

@@ -0,0 +1,20 @@
# Page 19 - Articoli Popup
## Items (16)
- `P19_COD_ARTICOLO`
- `P19_COD_CATEG`
- `P19_COD_RELATIVO`
- `P19_COEFF`
- `P19_COEFF_A`
- `P19_COEFF_B`
- `P19_COEFF_S`
- `P19_DESCRIZIONE`
- `P19_FLG_CUCINA`
- `P19_IMMAGINE`
- `P19_IMMAGINE_DISPLAY`
- `P19_QTA_STD_A`
- `P19_QTA_STD_B`
- `P19_QTA_STD_S`
- `P19_ROWID`
- `P19_TIPO_QTA`

View File

@@ -0,0 +1,11 @@
# Page 20 - Location
## Items (7)
- `P20_ID`
- `P20_INDIRIZZO`
- `P20_LOCATION`
- `P20_NOTE`
- `P20_NOTE2`
- `P20_REFERENTE`
- `P20_ROWID`

View File

@@ -0,0 +1,5 @@
# Page 21 - Costi Articolo
## Items (1)
- `P21_COD_ARTICOLO`

112
docs/apex/pages/PAGE_022.md Normal file
View File

@@ -0,0 +1,112 @@
# Page 22 - Evento
## Items (108)
- `P22_ALLERGIE`
- `P22_ALLEST_BUFF`
- `P22_ALTRO`
- `P22_ALTRO_A`
- `P22_ALTRO_B`
- `P22_ARTICOLO_ADD`
- `P22_ARTICOLO_REMOVE`
- `P22_BICCHIERI`
- `P22_BUFFET_DOLCI_A`
- `P22_BUFFET_DOLCI_B`
- `P22_BUFFET_FINALE`
- `P22_BUFFET_FINALE_DISPLAY`
- `P22_BUFFET_INIZIALE`
- `P22_BUFFET_INIZIALE_DISPLAY`
- `P22_CARICOAPOLL`
- `P22_CARICOSPOSI`
- `P22_CATEG`
- `P22_CATEG_FILTER`
- `P22_CLIENTE`
- `P22_CLIENTE_EMAIL`
- `P22_CLIENTE_TEL`
- `P22_COD_TIPO`
- `P22_COD_TIPO_FILTER`
- `P22_COLOR`
- `P22_CONSUNTIVO_MORE_DETAILS`
- `P22_CONTRATTO_FIRMATO`
- `P22_DATA`
- `P22_DATA_DOC`
- `P22_DATA_SCAD_PREVENTIVO`
- `P22_DATORASCARICO`
- `P22_DATORASCARICO_NOTE`
- `P22_DELETED`
- `P22_DELETED_BY`
- `P22_DELETED_DATE`
- `P22_DESCRIZIONE`
- `P22_DISABLED`
- `P22_DISTANZA_LOCATION`
- `P22_EVENT_ID`
- `P22_EVENT_ID_DISPLAY`
- `P22_EXTRA`
- `P22_EXTRA_COSTI`
- `P22_FLG_SUPERATO`
- `P22_FLG_TEMPLATE`
- `P22_GGSETT`
- `P22_GRAN_BUFFET_A`
- `P22_GRAN_BUFFET_B`
- `P22_ID_EVT_FIGLIO`
- `P22_ID_EVT_PADRE`
- `P22_ID_IMAGE_SHOW`
- `P22_INDIRIZZO`
- `P22_IS_TEMPLATE`
- `P22_LISTA_PRINT`
- `P22_LOCATION`
- `P22_LOCATION_DESCRI`
- `P22_MAIL_ENABLED`
- `P22_MAXSTEP`
- `P22_NEW`
- `P22_NEW_EVENT_ID`
- `P22_NOTE`
- `P22_NOTE_INVIO`
- `P22_NUMALTRI`
- `P22_NUMDEGUSTAZIONI`
- `P22_NUM_LISTA`
- `P22_ORA_CERIMONIA`
- `P22_ORA_EVENTO`
- `P22_ORA_FINE_CERIMONIA`
- `P22_ORA_FINE_EVENTO`
- `P22_ORA_FI_CER`
- `P22_ORA_FI_EVENTO`
- `P22_ORA_INI_CER`
- `P22_ORA_INI_EVENTO`
- `P22_PERC_SEDUTE`
- `P22_PERMES_CONSUNTIVI_REPORT`
- `P22_PIATTI`
- `P22_PIATTINO_PANE`
- `P22_POSATE`
- `P22_PRE_BOUV_A`
- `P22_PRE_BOUV_B`
- `P22_PRIMI`
- `P22_PRIMI_DISPLAY`
- `P22_REFERENTE_TEL`
- `P22_RISORSA_ADD`
- `P22_RISORSA_REMOVE`
- `P22_RUNNER`
- `P22_SCADUTO`
- `P22_SECONDI`
- `P22_SEDIA`
- `P22_SERVIZIO_TAVOLO_A`
- `P22_SERVIZIO_TAVOLO_B`
- `P22_SOTTOPIATTI`
- `P22_STATO`
- `P22_STATUS`
- `P22_STEP`
- `P22_STILE_COLORI`
- `P22_TIPOL_TAV_OSPITI`
- `P22_TIPOL_TAV_SPOSI`
- `P22_TIPORIS_FILTER`
- `P22_TORTA`
- `P22_TORTA_A`
- `P22_TORTA_B`
- `P22_TOT_OSPITI`
- `P22_TOVAGLIA`
- `P22_TOVAGLIOLO`
- `P22_VERSION_DISPLAY`
- `P22_VERS_NUMBER`
- `P22_VERS_TOKEN`
- `P22_VINI`
- `P22_VINI_DISPLAY`

View File

@@ -0,0 +1,6 @@
# Page 24 - Stampa Riepilogo Extra
## Items (2)
- `P24_DATA_FI`
- `P24_DATA_IN`

View File

@@ -0,0 +1,6 @@
# Page 25 - Cucina
## Items (2)
- `P25_DATA_FI`
- `P25_DATA_IN`

View File

@@ -0,0 +1,6 @@
# Page 26 - Stampa Riepilogo Cucina
## Items (2)
- `P26_DATA_FI`
- `P26_DATA_IN`

View File

@@ -0,0 +1,6 @@
# Page 27 - Degustazioni_Lista
## Items (2)
- `P27_DATA_FI`
- `P27_DATA_IN`

View File

@@ -0,0 +1,6 @@
# Page 28 - Torte e Costi Extra
## Items (2)
- `P28_DATA_FI`
- `P28_DATA_IN`

View File

@@ -0,0 +1,6 @@
# Page 29 - Stampa Riepilogo Allestimenti
## Items (2)
- `P29_DATA_FI`
- `P29_DATA_IN`

View File

@@ -0,0 +1,6 @@
# Page 30 - Riepilogo Allestimenti
## Items (2)
- `P30_DATA_FI`
- `P30_DATA_IN`

View File

@@ -0,0 +1,18 @@
# Page 32 - Degustazioni
## Items (14)
- `P32_DATA`
- `P32_EMAIL`
- `P32_ID`
- `P32_ID_EVENTO`
- `P32_LOCATION`
- `P32_MENU`
- `P32_NOME`
- `P32_NOTE`
- `P32_N_DEGUSTAZIONE`
- `P32_N_PAGANTI`
- `P32_N_PERSONE`
- `P32_ORA`
- `P32_ROWID`
- `P32_TELEFONO`

View File

@@ -0,0 +1,6 @@
# Page 33 - Stampa Scheda Evento
## Items (2)
- `P33_EVENT_ID`
- `P33_LISTA`

View File

@@ -0,0 +1,6 @@
# Page 35 - Lista Schede Evento
## Items (2)
- `P35_DATEFROM`
- `P35_DATETO`

View File

@@ -0,0 +1,6 @@
# Page 36 - Stampa Riepilogo Degustazioni
## Items (2)
- `P36_DATA_FI`
- `P36_DATA_IN`

View File

@@ -0,0 +1,7 @@
# Page 38 - Riepilogo Risorse
## Items (3)
- `P38_COD_AZIENDA`
- `P38_DATA_FI`
- `P38_DATA_IN`

View File

@@ -0,0 +1,8 @@
# Page 39 - Impegni Articoli
## Items (4)
- `P39_COD_ARTICOLO`
- `P39_DATA_FI`
- `P39_DATA_IN`
- `P39_SHOW_HISTORY`

View File

@@ -0,0 +1,7 @@
# Page 40 - Stampa Riepilogo Risorse
## Items (3)
- `P40_COD_AZIENDA`
- `P40_DATA_FI`
- `P40_DATA_IN`

View File

@@ -0,0 +1,5 @@
# Page 41 - Backup
## Items (1)
- `P41_BCKPASSWORD`

View File

@@ -0,0 +1,12 @@
# Page 42 - Nuovo Allegato Evento
## Items (8)
- `P42_CHARSET`
- `P42_EVENT_ID`
- `P42_FILENAME`
- `P42_FILESIZELIMITKB`
- `P42_ID`
- `P42_LAST_UPDATE`
- `P42_MIME_TYPE`
- `P42_RAW_DATA`

View File

@@ -0,0 +1,5 @@
# Page 43 - Registro Acconti Evento
## Items (1)
- `P43_ID_EVENTO`

View File

@@ -0,0 +1,5 @@
# Page 44 - Registro Altri Costi
## Items (1)
- `P44_ID_EVENTO`

View File

@@ -0,0 +1,8 @@
# Page 45 - GestioneDati
## Items (4)
- `P45_CONSUNTIVI_A`
- `P45_CONSUNTIVI_DA`
- `P45_EVENTI_A`
- `P45_EVENTI_DA`

View File

@@ -0,0 +1,6 @@
# Page 46 - Imposta Blocco Calendario
## Items (2)
- `P46_GIORNO`
- `P46_MAX_EVENTI`

View File

@@ -0,0 +1,6 @@
# Page 47 - Permessi
## Items (2)
- `P47_CONSUNTIVI_USERS`
- `P47_GESTORI_USERS`

View File

@@ -0,0 +1,10 @@
# Page 52 - send_mail_modal
## Items (6)
- `P52_DATA_FROM`
- `P52_DATA_FROM_DISPLAY`
- `P52_DATA_TO`
- `P52_DATA_TO_DISPLAY`
- `P52_MAIL_BODY`
- `P52_MAIL_SUBJECT`

View File

@@ -0,0 +1,11 @@
# Page 53 - COPIA_LISTA_PAGE
## Items (7)
- `P53_COPIA_ACCONTI`
- `P53_COPIA_ALTRICOSTI`
- `P53_COPIA_DEGUSTAZIONI`
- `P53_COPIA_PRELIEVI`
- `P53_COPIA_RISORSE`
- `P53_EVENTO_FROM`
- `P53_EVENTO_TO`

View File

@@ -0,0 +1,6 @@
# Page 101 - Login Page
## Items (2)
- `P101_PASSWORD`
- `P101_USERNAME`

View File

@@ -0,0 +1,7 @@
# Page 999 - Stampa Preventivo
## Items (3)
- `P999_EVENT_ID`
- `P999_MORE_DETAILS`
- `P999_SHOW_ECONOMICS`

View File

@@ -0,0 +1,558 @@
# APEX Processes Documentation
This document contains all 98 processes defined in the APEX application, organized by page.
## Process Overview
| Process Type | Count |
|--------------|-------|
| NATIVE_FORM_INIT | Multiple |
| NATIVE_FORM_DML | Multiple |
| NATIVE_PLSQL | Multiple |
| NATIVE_IG_DML | Multiple |
| NATIVE_SESSION_STATE | Multiple |
---
## Shared (Application-Level) Processes
### SET_USER_READONLY
**Process Point:** Before Header (runs on every page)
**Type:** NATIVE_PLSQL
Sets the `APP_READ_ONLY` application item based on user permissions.
```plsql
begin
if F_USER_IN_ROLE(:APP_USER, 'READONLY') then
:APP_READ_ONLY := 1;
else
:APP_READ_ONLY := 0;
end if;
end;
```
---
## Page 3 - Articoli (Article Form)
### Fetch Row from ARTICOLI
**Process Point:** After Header
**Type:** NATIVE_FORM_INIT
Initializes form with article data.
### Process Row of ARTICOLI
**Process Point:** After Submit
**Type:** NATIVE_FORM_DML
Saves/updates article record.
### Delete Image
**Process Point:** After Submit
**Type:** NATIVE_PLSQL
**Button:** Delete Image
```plsql
begin
update articoli
set
raw_data = null,
last_update = null,
charset = null,
mimetype = null,
filename = null
where rowid = :P3_ROWID;
end;
```
---
## Page 22 - Nuovo Evento (Main Event Form)
Page 22 is the most complex page with 32 processes.
### After Header Processes
#### Get Event Details
**Sequence:** 10
**Type:** NATIVE_FORM_INIT
Fetches event record into form items.
#### Set Ospiti on load
**Sequence:** 20
**Type:** NATIVE_PLSQL
```plsql
begin
EVENTI_AGGIORNA_TOT_OSPITI(:P22_EVENT_ID);
end;
```
#### Default Values
**Sequence:** 40
**Type:** NATIVE_PLSQL
```plsql
begin
select trim(l.location)
into :P22_LOCATION_DESCRI
from eventi e
join location l on l.id = e.id_location
where e.id = :P22_EVENT_ID;
exception when no_data_found then
null;
end;
```
#### Normalizza dati
**Sequence:** 50
**Type:** NATIVE_PLSQL
Formats time values for display:
```plsql
:P22_ORA_INI_CER := to_char(to_date(:P22_ORA_CERIMONIA, 'DD-MM-YYYY HH24:MI'), 'hh24:mi');
:P22_ORA_INI_EVENTO := to_char(to_date(:P22_ORA_EVENTO, 'DD-MM-YYYY HH24:MI'), 'hh24:mi');
:P22_ORA_FI_CER := to_char(to_date(:ORA_FINE_CERIMONIA, 'DD-MM-YYYY HH24:MI'), 'hh24:mi');
:P22_ORA_FI_EVENTO := to_char(to_date(:ORA_FINE_EVENTO, 'DD-MM-YYYY HH24:MI'), 'hh24:mi');
```
#### New Template Default Data
**Sequence:** 60
**Condition:** P22_IS_TEMPLATE is not null
```plsql
:P22_DATA := sysdate;
```
---
### After Submit Processes
#### Set Date Default if Template
**Sequence:** 10
**Condition:** P22_IS_TEMPLATE is not null
```plsql
:P22_DATA := sysdate;
```
#### Delete Template
**Sequence:** 20
**Button:** Delete Template
**Condition:** P22_IS_TEMPLATE is not null
```plsql
delete from eventi where id = :P22_EVENT_ID;
```
#### Genera Evento da Template
**Sequence:** 30
**Button:** Generate from Template
**Condition:** P22_IS_TEMPLATE is not null
```plsql
EVENTI_COPIA
(
ID_EVENTO_OLD => :P22_EVENT_ID,
NUOVA_VERSIONE => 0,
ID_EVENTO_NEW => :P22_EVENT_ID
);
```
#### Formatta Ore Inizio
**Sequence:** 40
Combines date and time for storage:
```plsql
:P22_ORA_EVENTO := :P22_DATA || ' ' || :P22_ORA_INI_EVENTO;
:P22_ORA_CERIMONIA := :P22_DATA || ' ' || :P22_ORA_INI_CER;
```
#### Prepara Acconti Automatici
**Sequence:** 50
**Condition:** REQUEST in ('PREPARA_ACCONTI', 'PRINT_PREVENTIVO')
```plsql
EVENTI_RICALCOLA_ACCONTI(p_event_id => :P22_EVENT_ID);
```
**Comment:** Default 3 deposits: 30%, 50%, 20%
#### Go Forward
**Sequence:** 60
**Button:** NEXT
Navigates to next wizard step:
```plsql
declare
v_step number;
begin
begin
select min(cod_step)
into v_step
from tb_tipi_mat
where cod_step > TO_NUMBER(:P22_STEP);
end;
begin
select cod_tipo
into :P22_COD_TIPO_FILTER
from tb_tipi_mat
where cod_step = v_step;
exception when no_data_found
then raise_application_error(-20001, 'Errore sconosciuto');
end;
:P22_STEP := v_step;
end;
```
#### Go Backward
**Sequence:** 70
**Button:** PREVIOUS
Navigates to previous wizard step.
#### Tipo Ospiti - Save Interactive Grid Data
**Sequence:** 80
**Region:** Guest Types Grid
**Type:** NATIVE_IG_DML
**Condition:** APP_READ_ONLY = 0
Saves guest type records.
#### Tipo Ospiti - Aggiorna Lista Prelievo
**Sequence:** 90
**Type:** NATIVE_PLSQL
**Condition:** APP_READ_ONLY = 0
Recalculates pick list quantities after guest changes:
```plsql
EVENTI_AGGIORNA_QTA_LISTA(
P_ID_EVENTO => :P22_EVENT_ID
);
```
#### Degustazioni - Save Interactive Grid Data
**Sequence:** 100
**Region:** Tastings Grid
**Type:** NATIVE_IG_DML
#### Delete Eventi Childs
**Sequence:** 110
**Button:** DELETE
**Condition:** NEVER (disabled)
```plsql
begin
delete from eventi_det_ospiti
where id_evento = :P22_EVENT_ID;
delete from eventi_det_prel
where id_evento = :P22_EVENT_ID;
delete from eventi_det_ris
where id_evento = :P22_EVENT_ID;
delete from eventi_det_degust
where id_evento = :P22_EVENT_ID;
end;
```
#### Set Obsoleto
**Sequence:** 120
**Button:** Set Obsolete
**Condition:** APP_READ_ONLY = 0
Marks event as expired/cancelled:
```plsql
begin
UPDATE EVENTI
SET FLG_SUPERATO = 1, STATO = 900, MAIL_ENABLED = 0
WHERE ID = :P22_EVENT_ID;
end;
```
#### Salva Evento
**Sequence:** 130
**Condition:** Complex (handles CREATE, SAVE, DELETE)
Main event save process. Full implementation handles:
- All event fields mapping
- Email validation
- Status-based logic
- Versioning support
- Soft delete
Key excerpts:
```plsql
declare
r_eventi eventi%rowtype;
begin
if :REQUEST = 'PREVIOUS' or :REQUEST = 'NEXT'
then
return;
end if;
if(:P22_DATA is null)
then
raise_application_error(-20001, 'Inserire la data evento');
end if;
-- Set all row fields
r_eventi."ID" := :P22_EVENT_ID;
r_eventi.DESCRIZIONE := :P22_DESCRIZIONE;
r_eventi.COD_TIPO := :P22_COD_TIPO;
-- ... (all other fields)
-- Email validation
IF REGEXP_LIKE(:P22_CLIENTE_EMAIL, '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$')
OR :P22_CLIENTE_EMAIL IS NULL THEN
r_eventi.CLIENTE_EMAIL := :P22_CLIENTE_EMAIL;
ELSE
RAISE_APPLICATION_ERROR(-20005, 'Formato email cliente non valido');
END IF;
-- Versioning
r_eventi.VERS_NUMBER := nvl(:P22_VERS_NUMBER, 0);
r_eventi.VERS_TOKEN := :P22_VERS_TOKEN;
case :REQUEST
when 'SAVE' then
update eventi set row = r_eventi where id = :P22_EVENT_ID;
when 'CREATE' then
insert into eventi values r_eventi returning id into :P22_EVENT_ID;
when 'DELETE' then
if r_eventi.ID_EVT_FIGLIO is not null or r_eventi.ID_EVT_PADRE is not null
then
raise_application_error(-20001, 'Impossibile eliminare un evento...');
end if;
update eventi
set deleted = 1,
deleted_by = :APP_USER,
deleted_date = sysdate
where id = r_eventi."ID";
else
null;
end case;
end;
```
#### Nuova Versione
**Sequence:** 140
**Button:** New Version
Creates a new version of the event:
```plsql
EVENTI_COPIA
(
ID_EVENTO_OLD => :P22_EVENT_ID,
NUOVA_VERSIONE => 1,
ID_EVENTO_NEW => :P22_NEW_EVENT_ID
);
```
#### Lista Prelievo - Save Interactive Grid Data
**Sequence:** 150
**Type:** NATIVE_IG_DML
**Table:** EVENTI_DET_PREL
Saves pick list records.
#### Continue Event
**Sequence:** 160
**Button:** Continue
Advances event to "Confermata" status (300):
```plsql
declare
v_count number;
v_count_evt number;
begin
-- Check if confirmed event exists for same location/date
select count(*) into v_count
from eventi
where data = (select data from eventi where id = :P22_EVENT_ID)
and stato = 300
and id_location = (select id_location from eventi where id = :P22_EVENT_ID);
if v_count > 0 then
raise_application_error(-20001, 'Esiste un evento Confermato per la location - Impossibile proseguire');
return;
end if;
-- Update status
update eventi
set stato = 300
where id = :P22_EVENT_ID;
-- Check total confirmed events for date
select count(*) into v_count_evt
from eventi
where data = (select data from eventi where id = :P22_EVENT_ID)
and stato = 300;
if v_count_evt > 6 then
RAISE_APPLICATION_ERROR(-20000, 'Ci sono già 6 eventi Confermati per la data');
end if;
end;
```
#### Almost Continue Event
**Sequence:** 170
**Button:** Almost Continue
Sets status to "Quasi Confermato" (350):
```plsql
begin
update eventi
set stato = 350
where id = :P22_EVENT_ID;
end;
```
#### Confirm Event
**Sequence:** 180
**Button:** Confirm
**Condition:** APP_READ_ONLY = 0
Final confirmation (status 400) and cancels competing events:
```plsql
begin
update eventi
set stato = 400
where id = :P22_EVENT_ID;
-- Cancel events with same date and location
p_cancel_same_location_events(
p_good_event_id => :P22_EVENT_ID
);
end;
```
#### Unconfirm Event
**Sequence:** 190
**Button:** Unconfirm
Returns event to initial status (100):
```plsql
begin
update eventi
set stato = 100
where id = :P22_EVENT_ID;
end;
```
#### Reopen Event
**Sequence:** 200
**Button:** Reopen
Clears the expired flag:
```plsql
begin
update eventi
set flg_superato = 0
where id = :P22_EVENT_ID;
end;
```
#### Return to Preparazione
**Sequence:** 210
**Button:** Return to Preparation
Sets status back to preparation (200):
```plsql
begin
update eventi
set stato = 200
where id = :P22_EVENT_ID;
end;
```
#### Risorse - Save Interactive Grid Data
**Sequence:** 220
**Type:** NATIVE_IG_DML
Saves resource assignments.
#### Aggiorna QTA Lista
**Sequence:** 230
**Condition:** APP_READ_ONLY = 0 AND REQUEST = 'AGGIORNA_QTA'
Manual quantity recalculation:
```plsql
EVENTI_AGGIORNA_QTA_LISTA(
P_ID_EVENTO => :P22_EVENT_ID
);
```
#### Restore Deleted Event
**Sequence:** 240
Restores soft-deleted event.
---
## Event Status Workflow Summary
| Status Code | Status Name | Italian | Next Action |
|-------------|-------------|---------|-------------|
| 100 | Preventivo | Quote | Continue Event → 300 |
| 200 | Scheda | Preparation | - |
| 300 | Confermata | Confirmed (Pending) | Almost Continue → 350 |
| 350 | Quasi | Almost Confirmed | Confirm → 400 |
| 400 | Confermato | Confirmed | - |
| 900 | Superato | Expired/Cancelled | Reopen → clears flag |
---
## Common Process Patterns
### Read-Only Check
All write processes include this condition:
```plsql
:APP_READ_ONLY = 0
```
### Error Handling
Standard error pattern:
```plsql
exception when no_data_found then
raise_application_error(-20001, 'Error message in Italian');
```
### Interactive Grid Save
Standard IG DML process:
- Type: NATIVE_IG_DML
- Attributes: REGION_SOURCE, Allow Insert (Y), Allow Update (Y), Allow Delete (Y)
---
## Migration Notes
When migrating these processes to .NET:
1. **Form Initialization** - Use API endpoints to fetch entity data
2. **Form DML** - Implement CRUD endpoints with proper validation
3. **Custom PL/SQL** - Convert to C# methods or stored procedures
4. **IG DML** - Implement batch update endpoints for grid data
5. **Session State** - Use application state management (Redux, Context)
6. **Validation** - Implement in both frontend and backend
7. **Workflow** - Consider state machine pattern for event status

View File

@@ -0,0 +1,22 @@
# CLOB2BLOB
## Codice Sorgente
```sql
FUNCTION "CLOB2BLOB" (AClob CLOB) return BLOB is
Result BLOB;
o1 integer;
o2 integer;
c integer;
w integer;
begin
o1 := 1;
o2 := 1;
c := 0;
w := 0;
DBMS_LOB.CreateTemporary(Result, true);
DBMS_LOB.ConvertToBlob(Result, AClob, length(AClob), o1, o2, 0, c, w);
return(Result);
end clob2blob;
```

View File

@@ -0,0 +1,10 @@
# EXTDATE_GET_ITA
## Codice Sorgente
```sql
function extdate_get_ita( p_date date ) return varchar2
as
begin
return REGEXP_REPLACE(TO_CHAR(p_date, 'Day dd Month yyyy', 'NLS_DATE_LANGUAGE = ITALIAN'), ' [ ]+', ' ');
end;```

View File

@@ -0,0 +1,37 @@
# F_CI_SONO_EVENTI_CONFERMATI
## Codice Sorgente
```sql
FUNCTION F_CI_SONO_EVENTI_CONFERMATI
(
P_EVT_DATE IN DATE
, P_LOCATION_ID IN NUMBER
, P_BYPASS IN NUMBER
) RETURN NUMBER AS
v_evt_cnt number;
BEGIN
-- function bypass
if P_BYPASS > 0 then
return 0;
end if;
select
count(e.id) as evt_cnt
into v_evt_cnt
from eventi e
join vw_event_color c on c.id = e.id
where e.data = P_EVT_DATE
and e.id_location = P_LOCATION_ID
and c.status = 'Confermato';
if v_evt_cnt > 0 then
return 1;
else
return 0;
end if;
END F_CI_SONO_EVENTI_CONFERMATI;```

View File

@@ -0,0 +1,45 @@
# F_DAY_TO_NAME
## Codice Sorgente
```sql
FUNCTION F_DAY_TO_NAME
(
DAY_NUMBER IN NUMBER
) RETURN VARCHAR2 AS
v_day_number number := DAY_NUMBER;
v_language varchar2(255);
BEGIN
with t as (
select DECODE(parameter, 'NLS_CHARACTERSET', 'CHARACTER SET',
'NLS_LANGUAGE', 'LANGUAGE',
'NLS_TERRITORY', 'TERRITORY') name,
value from v$nls_parameters
WHERE parameter IN ( 'NLS_CHARACTERSET', 'NLS_LANGUAGE', 'NLS_TERRITORY')
)
select value into v_language
from t
where name = 'LANGUAGE';
if v_language = 'AMERICAN' then
case v_day_number
when 1 then return 'Domenica';
when 2 then return 'Lunedì';
when 3 then return 'Martedì';
when 4 then return 'Mercoledì';
when 5 then return 'Giovedì';
when 6 then return 'Venerdì';
when 7 then return 'Sabato';
end case;
else
case v_day_number
when 1 then return 'Lunedì';
when 2 then return 'Martedì';
when 3 then return 'Mercoledì';
when 4 then return 'Giovedì';
when 5 then return 'Venerdì';
when 6 then return 'Sabato';
when 7 then return 'Domenica';
end case;
end if;
END F_DAY_TO_NAME;```

View File

@@ -0,0 +1,22 @@
# F_EVENTO_SCADUTO
## Codice Sorgente
```sql
FUNCTION F_EVENTO_SCADUTO
(
DATA_SCADENZA IN DATE,
STATO_EVENTO IN NUMBER,
STATO_FROM IN NUMBER,
STATO_TO IN NUMBER
) RETURN NUMBER AS
BEGIN
if trunc(DATA_SCADENZA) <= trunc(sysdate)
and STATO_EVENTO between STATO_FROM and STATO_TO
then
return 1;
else
return 0;
end if;
END F_EVENTO_SCADUTO;
```

View File

@@ -0,0 +1,31 @@
# F_GET_ANGOLO_ALLESTIMENTO
## Codice Sorgente
```sql
FUNCTION "F_GET_ANGOLO_ALLESTIMENTO" (
p_filtro VARCHAR2,
p_id NUMBER
) RETURN VARCHAR2 AS
TYPE ref_cur IS REF CURSOR;
c_data ref_cur;
v_val VARCHAR2(1000);
v_filtro VARCHAR2(100);
v_id NUMBER;
BEGIN
v_filtro := p_filtro;
v_id := p_id;
OPEN c_data FOR ' select substr(a.descrizione || '' - '' || p.note ,1,1000)
from eventi e
left join location l on e.id_location = l.id
join eventi_det_prel p on e.id=p.id_evento
join articoli a on p.cod_articolo=a.cod_articolo
where p.COD_ARTICOLO = :filtro -- AN-GELAT
and e.id = to_number(:id)'
USING v_filtro, v_id;
FETCH c_data INTO v_val;
CLOSE c_data;
RETURN v_val;
END;```

View File

@@ -0,0 +1,34 @@
# F_GET_ANGOLO_ALLESTIMENTO_OB
## Codice Sorgente
```sql
FUNCTION "F_GET_ANGOLO_ALLESTIMENTO_OB" (
p_filtro VARCHAR2,
p_id NUMBER
) RETURN VARCHAR2
--ANGOLI OPEN BAR
AS
TYPE ref_cur IS REF CURSOR;
c_data ref_cur;
v_val VARCHAR2(1000);
v_filtro VARCHAR2(100);
v_id NUMBER;
BEGIN
v_filtro := p_filtro;
v_id := p_id;
OPEN c_data FOR ' select substr(a.descrizione || '' - '' || p.note ,1,1000)
from eventi e
left join location l on e.id_location = l.id
join eventi_det_prel p on e.id=p.id_evento
join articoli a on p.cod_articolo=a.cod_articolo
where a.cod_categ = ''AN-FIN''
and a.descrizione like :filtro -- AN-GELAT
and e.id = to_number(:id)'
USING v_filtro, v_id;
FETCH c_data INTO v_val;
CLOSE c_data;
RETURN v_val;
END;```

View File

@@ -0,0 +1,37 @@
# F_GET_COSTO_ARTICOLO
## Codice Sorgente
```sql
function f_get_costo_articolo(p_cod_articolo varchar2, p_date date)
return number
as
v_costo number := null;
begin
-- Cerco il costo alla data
begin
SELECT a.costo_uni
into v_costo
FROM COSTI_ARTICOLI a
where a.data_costo = p_date
and a.cod_articolo = p_cod_articolo;
exception when no_data_found then
v_costo := null;
end;
-- Se non lo trovo prendo l'ultimo costo
begin
SELECT a.costo_uni
into v_costo
FROM COSTI_ARTICOLI a
WHERE a.DATA_COSTO = (SELECT max(b.DATA_COSTO) FROM COSTI_ARTICOLI b where b.cod_articolo = a.cod_articolo)
and a.cod_articolo = p_cod_articolo;
exception when no_data_found then
v_costo := null;
end;
-- se non trovo niente torno 0
return nvl(v_costo, 0);
end;
```

View File

@@ -0,0 +1,164 @@
# F_GET_OSPITI
## Codice Sorgente
```sql
FUNCTION "F_GET_OSPITI" (
p_id_evento IN NUMBER
) RETURN t_det_ospiti_tab
PIPELINED
AS
v_data DATE;
v_location VARCHAR2(100);
v_cliente VARCHAR2(100);
v_descrizione VARCHAR2(100);
v_ora_cerimonia VARCHAR2(10);
v_ora_evento VARCHAR2(10);
v_tot_adulti NUMBER;
v_tot_kinder NUMBER;
v_tot_baby NUMBER;
v_tot_staff NUMBER;
v_tot_invitati NUMBER;
v_allergie VARCHAR2(4000);
v_torta VARCHAR2(1000);
v_confettata VARCHAR2(100);
v_stampa_menu VARCHAR2(100);
v_angoli VARCHAR2(1000);
v_extra_info VARCHAR2(1000);
v_note_adulti VARCHAR2(1000);
v_note_kinder VARCHAR2(1000);
v_note_baby VARCHAR2(1000);
v_note_staff VARCHAR2(1000);
BEGIN
BEGIN
v_tot_adulti := 0;
v_tot_kinder := 0;
v_tot_baby := 0;
v_tot_staff := 0;
v_tot_invitati := 0;
v_angoli := 0;
v_note_adulti := '';
v_note_kinder := '';
v_note_baby := '';
v_note_staff := '';
--ospiti
SELECT
nvl(SUM(o.numero),
0)
INTO v_tot_adulti
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 8; -- adulti
BEGIN
SELECT
o.note
INTO v_note_adulti
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 8; -- adulti
EXCEPTION
WHEN no_data_found THEN
NULL;
END;
SELECT
nvl(SUM(o.numero),
0)
INTO v_tot_kinder
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 5; --Kinder
BEGIN
SELECT
o.note
INTO v_note_kinder
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 5; --Kinder
EXCEPTION
WHEN no_data_found THEN
NULL;
END;
SELECT
nvl(SUM(o.numero),
0)
INTO v_tot_baby
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 6; -- Baby
BEGIN
SELECT
o.note
INTO v_note_baby
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 6; -- Baby
EXCEPTION
WHEN no_data_found THEN
NULL;
END;
SELECT
nvl(SUM(o.numero),
0)
INTO v_tot_staff
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 7; -- Staff
BEGIN
SELECT
o.note
INTO v_note_staff
FROM
eventi_det_ospiti o
WHERE
o.id_evento = p_id_evento
AND o.cod_tipo_ospite = 7; -- Staff
EXCEPTION
WHEN no_data_found THEN
NULL;
END;
SELECT
nvl(e.tot_ospiti, 0)
INTO v_tot_invitati
FROM
eventi e
WHERE
e.id = p_id_evento;
PIPE ROW ( t_det_ospiti_row(p_id_evento, v_tot_adulti, v_tot_kinder, v_tot_baby, v_tot_staff,
v_tot_invitati, v_note_adulti, v_note_kinder, v_note_baby, v_note_staff) );
EXCEPTION
WHEN OTHERS THEN
NULL;
END;
RETURN;
END;```

View File

@@ -0,0 +1,36 @@
# F_GET_QTA_IMPEGNATA
## Codice Sorgente
```sql
FUNCTION "F_GET_QTA_IMPEGNATA" (
p_codart VARCHAR2,
p_data_from DATE,
p_data_to DATE DEFAULT NULL
) RETURN NUMBER AS
v_qta NUMBER;
v_data_to DATE := p_data_to;
BEGIN
v_qta := 0;
IF ( p_data_to IS NULL ) THEN
v_data_to := p_data_from;
END IF;
BEGIN
SELECT
nvl(SUM(qta_imp),
0)
INTO v_qta
FROM
v_impegni_articoli
WHERE
cod_articolo = p_codart
--and data <= p_data --
AND data BETWEEN p_data_from AND v_data_to; -- qta impegnata per quella data
EXCEPTION
WHEN OTHERS THEN
v_qta := 0;
END;
RETURN v_qta;
END;```

View File

@@ -0,0 +1,31 @@
# F_GET_TOT_OSPITI
## Codice Sorgente
```sql
FUNCTION "F_GET_TOT_OSPITI" (
p_id_evento IN NUMBER,
p_tipo_ospite IN NUMBER := -1
) RETURN NUMBER AS
v_tot NUMBER;
BEGIN
v_tot := 0;
BEGIN
SELECT
nvl(SUM(numero),
0)
INTO v_tot
FROM
eventi_det_ospiti
WHERE
id_evento = p_id_evento
AND ( cod_tipo_ospite = p_tipo_ospite
OR p_tipo_ospite = - 1 );
EXCEPTION
WHEN OTHERS THEN
v_tot := 0;
END;
RETURN v_tot;
END;```

View File

@@ -0,0 +1,34 @@
# F_GET_TOVAGLIATO_ALLESTIMENTO
## Codice Sorgente
```sql
FUNCTION "F_GET_TOVAGLIATO_ALLESTIMENTO" (
p_filtro VARCHAR2,
p_id NUMBER
) RETURN VARCHAR2 AS
TYPE ref_cur IS REF CURSOR;
c_data ref_cur;
v_val VARCHAR2(1000);
v_filtro VARCHAR2(100);
v_id NUMBER;
BEGIN
v_filtro := p_filtro;
v_id := p_id;
OPEN c_data FOR ' select substr(a.descrizione || '' - '' || p.note ,1,1000) as dato
from eventi e
left join location l on e.id_location = l.id
join eventi_det_prel p on e.id=p.id_evento
join articoli a on p.cod_articolo=a.cod_articolo
join TB_CODICI_CATEG c on a.cod_categ=c.cod_categ
--where c.COD_TIPO = ''TVB'' -- dividere tovagliolo da tovagliato con i codici categ
where c.COD_TIPO = :filtro
and rownum = 1 -- se esistono più articoli fare list_agg op loop
and e.id = to_number(:id)'
USING v_filtro, v_id;
FETCH c_data INTO v_val;
CLOSE c_data;
RETURN v_val;
END;```

View File

@@ -0,0 +1,84 @@
# F_LIST_PRELIEVO_ADD_ARTICOLO
## Codice Sorgente
```sql
function f_list_prelievo_add_articolo(
p_event_id number,
p_articolo_add varchar2,
p_qta_aperitivo NUMBER := 0,
p_qta_seduto NUMBER := 0,
p_qta_dolci NUMBER := 0
)
return varchar2
as
v_qta_imp number;
v_qta_giac number;
v_data_evento date;
v_cod_art varchar2(10);
v_error_json varchar2(4000);
v_qta_da_imp NUMBER;
v_qta_da_imp_test NUMBER;
v_num_evt_imp number;
BEGIN
v_cod_art := p_articolo_add;
v_qta_da_imp := nvl(p_qta_aperitivo, 0) + nvl(p_qta_seduto, 0) + nvl(p_qta_dolci, 0);
begin
select trunc(data) as data
into v_data_evento
from eventi where id = p_event_id;
exception when no_data_found then
rollback;
RETURN '{"type":"error","code":"'||SQLCODE||'","stack":"'||SQLERRM||'","message":"Evento non trovato"}';
end;
begin
select qta_giac
into v_qta_giac
from articoli
where COD_ARTICOLO = v_cod_art;
exception when no_data_found then
rollback;
RETURN '{"type":"error","code":"'||SQLCODE||'","stack":"'||SQLERRM||'","message":"Articolo non trovato"}';
end;
select count(*)
into v_num_evt_imp
from V_IMPEGNI_ARTICOLI
where COD_ARTICOLO = v_cod_art
and data between v_data_evento - 2 and v_data_evento + 2;
v_qta_imp := nvl(f_get_qta_impegnata (v_cod_art, v_data_evento - 2, v_data_evento + 2 ) , 0);
--insert impegno articolo
begin
insert into eventi_det_prel (id_evento, cod_articolo,QTA_MAN_APE,QTA_MAN_SEDU,QTA_MAN_BUFDOL, COSTO_ARTICOLO)
values (p_event_id, p_articolo_add, p_qta_aperitivo, p_qta_seduto, p_qta_dolci, f_get_costo_articolo(v_cod_art, v_data_evento));
-- aggiorna liste prelievo
EVENTI_AGGIORNA_QTA_LISTA(
P_ID_EVENTO => p_event_id
);
-- Controlla banalmente se sono stati prelevati su altri eventi
if v_qta_imp + v_qta_da_imp > v_qta_giac then
RETURN '{"type":"warning","message":"Attenzione: Non hai abbastanza articoli di questo tipo a magazzino"}';
end if;
-- Se trovo articoli già impegnati in quel giorno mostro un messaggio - 16/11/2022
-- Continuo lo stesso ma do un messaggio di errore anzichè di successo
if(v_qta_imp > 0 and v_num_evt_imp > 0) then
RETURN '{"type":"warning","code":"'||SQLCODE||'","stack":"'||SQLERRM||'","message":"Attenzione! Articolo '||p_articolo_add||' già impegnato '||v_num_evt_imp||' '||(case when v_num_evt_imp > 1 then 'volte' else 'volta' end)||' dal '||to_char(v_data_evento - 2, 'dd-mm-yyyy')||' al '||to_char(v_data_evento + 2, 'dd-mm-yyyy')||'"}';
end if;
RETURN '{"type":"success","message":"Articolo aggiunto con successo"}';
exception when others then
RETURN '{"type":"success","message":"Articolo aggiunto con errori: '||replace(replace(SQLERRM, 'ORA-20000: Errore:', ''), '-20000 - ', '')||'"}';
end;
rollback;
return '{"type":"error","code":"","stack":"","message":"Errore sconosciuto"}';
end f_list_prelievo_add_articolo;```

View File

@@ -0,0 +1,38 @@
# F_MAX_NUMERO_EVENTI_RAGGIUNTO
## Codice Sorgente
```sql
FUNCTION F_MAX_NUMERO_EVENTI_RAGGIUNTO
(
P_GIORNO IN DATE
) RETURN NUMBER AS
v_max_eventi TB_CALENDAR_LOCKS.max_eventi%TYPE; -- Variable to hold the max_eventi value from TB_CALENDAR_LOCKS
v_event_count NUMBER; -- Variable to hold the count of events from EVENTI
BEGIN
-- Step 1: Check if P_GIORNO is present in TB_CALENDAR_LOCKS and get the max_eventi value for that day
BEGIN
SELECT max_eventi
INTO v_max_eventi
FROM TB_CALENDAR_LOCKS
WHERE giorno = P_GIORNO;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- If the date is not found in TB_CALENDAR_LOCKS, return -1 (or some other code to indicate the absence)
RETURN -1;
END;
-- Step 2: Count how many events occurred on P_GIORNO in the EVENTI table
SELECT COUNT(*)
INTO v_event_count
FROM EVENTI
WHERE TRUNC(DATA) = TRUNC(P_GIORNO); -- Use TRUNC to compare only the date part
-- Step 3: Compare the event count with the max_eventi and return the appropriate result
IF v_event_count >= v_max_eventi THEN
RETURN 1; -- Maximum number of events has been reached or exceeded
ELSE
RETURN 0; -- Maximum number of events has not been reached
END IF;
END F_MAX_NUMERO_EVENTI_RAGGIUNTO;```

View File

@@ -0,0 +1,36 @@
# F_MAX_NUM_EVENTI_CONFERMATI
## Codice Sorgente
```sql
FUNCTION F_MAX_NUM_EVENTI_CONFERMATI
(
P_EVT_DATE IN DATE
, P_MAX_EVT_NUM NUMBER
, P_BYPASS IN NUMBER
) RETURN NUMBER AS
v_evt_cnt number;
BEGIN
-- function bypass
if P_BYPASS > 0 then
return 0;
end if;
select
count(e.id) as evt_cnt
into v_evt_cnt
from eventi e
join vw_event_color c on c.id = e.id
where e.data = P_EVT_DATE
and c.status = 'Confermato';
if v_evt_cnt >= P_MAX_EVT_NUM then
return 1;
else
return 0;
end if;
END F_MAX_NUM_EVENTI_CONFERMATI;```

View File

@@ -0,0 +1,433 @@
# F_REP_ALLESTIMENTI
## Codice Sorgente
```sql
FUNCTION "F_REP_ALLESTIMENTI" ( p_data_in IN varchar2 default to_char(sysdate,'YYYYMMD'),
p_data_fi IN varchar2 default to_char(sysdate + 30,'YYYYMMD')
)
RETURN t_rep_allestimenti_tab PIPELINED AS
v_data_in varchar2(100);
v_data_fi varchar2(100);
cursor c_evento is
with t as (--select e.id, e.data, l.location, e.torta, e.confettata, e.stampa_menu
select e.id, e.data, l.location, e.torta,
e.altro_a as confettata,
e.sedia as stampa_menu
from eventi e
left join location l on e.id_location = l.id
--where data >= sysdate
where e.data between to_date(v_data_in,'YYYYMMDD') and to_date(v_data_fi,'YYYYMMDD')
and e.stato in (300, 400) -- 300 Scheda Confermata, 400 Confermato (Lista)
and e.disabled = 0
and e.deleted = 0
order by data, to_number(to_char(e.ora_evento,'HH24MI'))
)
, q as (select t.* , rownum as order_id
from t
order by data
)
, r as (select
case when order_id =1 then id else null end as id1,
case when order_id =2 then id else null end as id2,
case when order_id =3 then id else null end as id3,
case when order_id =4 then id else null end as id4,
case when order_id =5 then id else null end as id5,
case when order_id =6 then id else null end as id6,
case when order_id =7 then id else null end as id7,
case when order_id =8 then id else null end as id8,
case when order_id =9 then id else null end as id9,
case when order_id =10 then id else null end as id10,
case when order_id =11 then id else null end as id11,
case when order_id =12 then id else null end as id12,
case when order_id =1 then data else null end as data1,
case when order_id =2 then data else null end as data2,
case when order_id =3 then data else null end as data3,
case when order_id =4 then data else null end as data4,
case when order_id =5 then data else null end as data5,
case when order_id =6 then data else null end as data6,
case when order_id =7 then data else null end as data7,
case when order_id =8 then data else null end as data8,
case when order_id =9 then data else null end as data9,
case when order_id =10 then data else null end as data10,
case when order_id =11 then data else null end as data11,
case when order_id =12 then data else null end as data12,
case when order_id =1 then location else null end as location1,
case when order_id =2 then location else null end as location2,
case when order_id =3 then location else null end as location3,
case when order_id =4 then location else null end as location4,
case when order_id =5 then location else null end as location5,
case when order_id =6 then location else null end as location6,
case when order_id =7 then location else null end as location7,
case when order_id =8 then location else null end as location8,
case when order_id =9 then location else null end as location9,
case when order_id =10 then location else null end as location10,
case when order_id =11 then location else null end as location11,
case when order_id =12 then location else null end as location12,
case when order_id =1 then torta else null end as torta1,
case when order_id =2 then torta else null end as torta2,
case when order_id =3 then torta else null end as torta3,
case when order_id =4 then torta else null end as torta4,
case when order_id =5 then torta else null end as torta5,
case when order_id =6 then torta else null end as torta6,
case when order_id =7 then torta else null end as torta7,
case when order_id =8 then torta else null end as torta8,
case when order_id =9 then torta else null end as torta9,
case when order_id =10 then torta else null end as torta10,
case when order_id =11 then torta else null end as torta11,
case when order_id =12 then torta else null end as torta12,
case when order_id =1 then confettata else null end as confettata1,
case when order_id =2 then confettata else null end as confettata2,
case when order_id =3 then confettata else null end as confettata3,
case when order_id =4 then confettata else null end as confettata4,
case when order_id =5 then confettata else null end as confettata5,
case when order_id =6 then confettata else null end as confettata6,
case when order_id =7 then confettata else null end as confettata7,
case when order_id =8 then confettata else null end as confettata8,
case when order_id =9 then confettata else null end as confettata9,
case when order_id =10 then confettata else null end as confettata10,
case when order_id =11 then confettata else null end as confettata11,
case when order_id =12 then confettata else null end as confettata12,
case when order_id =1 then stampa_menu else null end as stampa_menu1,
case when order_id =2 then stampa_menu else null end as stampa_menu2,
case when order_id =3 then stampa_menu else null end as stampa_menu3,
case when order_id =4 then stampa_menu else null end as stampa_menu4,
case when order_id =5 then stampa_menu else null end as stampa_menu5,
case when order_id =6 then stampa_menu else null end as stampa_menu6,
case when order_id =7 then stampa_menu else null end as stampa_menu7,
case when order_id =8 then stampa_menu else null end as stampa_menu8,
case when order_id =9 then stampa_menu else null end as stampa_menu9,
case when order_id =10 then stampa_menu else null end as stampa_menu10,
case when order_id =11 then stampa_menu else null end as stampa_menu11,
case when order_id =12 then stampa_menu else null end as stampa_menu12
from q
)
select min(id1) as id1,
min(id2) as id2,
min(id3) as id3,
min(id4) as id4,
min(id5) as id5,
min(id6) as id6,
min(id7) as id7,
min(id8) as id8,
min(id9) as id9,
min(id10) as id10,
min(id11) as id11,
min(id12) as id12,
min(data1) as d1,
min(data2) as d2,
min(data3) as d3,
min(data4) as d4,
min(data5) as d5,
min(data6) as d6,
min(data7) as d7,
min(data8) as d8,
min(data9) as d9,
min(data10) as d10,
min(data11) as d11,
min(data12) as d12,
min(location1) as l1,
min(location2) as l2,
min(location3) as l3,
min(location4) as l4,
min(location5) as l5,
min(location6) as l6,
min(location7) as l7,
min(location8) as l8,
min(location9) as l9,
min(location10) as l10,
min(location11) as l11,
min(location12) as l12,
min(torta1) as t1,
min(torta2) as t2,
min(torta3) as t3,
min(torta4) as t4,
min(torta5) as t5,
min(torta6) as t6,
min(torta7) as t7,
min(torta8) as t8,
min(torta9) as t9,
min(torta10) as t10,
min(torta11) as t11,
min(torta12) as t12,
min(confettata1) as c1,
min(confettata2) as c2,
min(confettata3) as c3,
min(confettata4) as c4,
min(confettata5) as c5,
min(confettata6) as c6,
min(confettata7) as c7,
min(confettata8) as c8,
min(confettata9) as c9,
min(confettata10) as c10,
min(confettata11) as c11,
min(confettata12) as c12,
min(stampa_menu1) as SM1,
min(stampa_menu2) as SM2,
min(stampa_menu3) as SM3,
min(stampa_menu4) as SM4,
min(stampa_menu5) as SM5,
min(stampa_menu6) as SM6,
min(stampa_menu7) as SM7,
min(stampa_menu8) as SM8,
min(stampa_menu9) as SM9,
min(stampa_menu10) as SM10,
min(stampa_menu11) as SM11,
min(stampa_menu12) as SM12
from r;
c_evt c_evento%ROWTYPE;
type v_TOVAGLIATO_AR IS VARRAY(12) OF VARCHAR2(4000);
v_TOVAGLIATO v_TOVAGLIATO_AR;
C1 varchar2(100);
C2 varchar2(100);
C3 varchar2(100);
C4 varchar2(100);
C5 varchar2(100);
C6 varchar2(100);
C7 varchar2(100);
C8 varchar2(100);
C9 varchar2(100);
C10 varchar2(100);
C11 varchar2(100);
C12 varchar2(100);
type v_TOVAGLIOLO_AR IS VARRAY(12) OF VARCHAR2(1000);
v_TOVAGLIOLO v_TOVAGLIOLO_AR;
type v_AN_GELATO_AR IS VARRAY(12) OF VARCHAR2(1000);
v_AN_GELATO v_AN_GELATO_AR;
type v_AN_GELATO2_AR IS VARRAY(12) OF VARCHAR2(1000);
v_AN_GELATO2 v_AN_GELATO2_AR;
type v_AN_OPENBAR_AR IS VARRAY(12) OF VARCHAR2(1000);
v_AN_OPENBAR v_AN_OPENBAR_AR;
type v_AN_RUM_AR IS VARRAY(12) OF VARCHAR2(1000);
v_AN_RUM v_AN_RUM_AR;
T1 varchar2(100);
T2 varchar2(100);
T3 varchar2(100);
T4 varchar2(100);
T5 varchar2(100);
T6 varchar2(100);
T7 varchar2(100);
T8 varchar2(100);
T9 varchar2(100);
T10 varchar2(100);
T11 varchar2(100);
T12 varchar2(100);
v_appo varchar2(100);
v_dato varchar2(100);
v_id_str varchar2(100);
v_id number;
v_qry varchar2(1000);
type v_IDEVT_AR IS VARRAY(12) OF number;
v_IDEVT v_IDEVT_AR;
BEGIN
--default su date
if p_data_in is null then
v_data_in := to_char(sysdate,'YYYYMMD');
else
v_data_in := p_data_in;
end if;
if p_data_fi is null then
v_data_fi := to_char(sysdate + 30,'YYYYMMD');
else
v_data_fi := p_data_fi;
end if;
v_TOVAGLIATO := v_TOVAGLIATO_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
v_TOVAGLIOLO := v_TOVAGLIOLO_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
v_AN_GELATO := v_AN_GELATO_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
v_AN_GELATO2 := v_AN_GELATO2_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
v_AN_OPENBAR := v_AN_OPENBAR_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
v_AN_RUM := v_AN_RUM_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
v_IDEVT := v_IDEVT_AR(NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL);
open c_evento;
fetch c_evento into c_evt;
--exit when c_evento%NOTFOUND;
--caricare su vettore gli id:
v_IDEVT(1) := c_evt.id1;
v_IDEVT(2) := c_evt.id2;
v_IDEVT(3) := c_evt.id3;
v_IDEVT(4) := c_evt.id4;
v_IDEVT(5) := c_evt.id5;
v_IDEVT(6) := c_evt.id6;
v_IDEVT(7) := c_evt.id7;
v_IDEVT(8) := c_evt.id8;
v_IDEVT(9) := c_evt.id9;
v_IDEVT(10) := c_evt.id10;
v_IDEVT(11) := c_evt.id11;
v_IDEVT(12) := c_evt.id12;
FOR i in 1 .. 12 LOOP
v_dato := '';
v_id := to_char(v_IDEVT(i));
v_TOVAGLIATO(i) := f_get_tovagliato_allestimento ('TVB', v_IDEVT(i) ) ;
v_AN_GELATO(i) := f_get_angolo_allestimento ('AN-GELAT', v_IDEVT(i) ) ;
v_AN_GELATO2(i) := f_get_angolo_allestimento ('AN-GEL-BOM', v_IDEVT(i) ) ;
--v_AN_OPENBAR(i) := f_get_angolo_allestimento ('OPEN-B', v_IDEVT(i) ) ;
v_AN_OPENBAR(i) := f_get_angolo_allestimento_OB ('%OPEN BAR%', v_IDEVT(i) ) ;
v_AN_RUM(i) := f_get_angolo_allestimento ('AN-RUM-CI', v_IDEVT(i) ) ;
END LOOP;
PIPE ROW(t_rep_allestimenti_row(c_evt.D1 ,
c_evt.D2 ,
c_evt.D3 ,
c_evt.D4 ,
c_evt.D5 ,
c_evt.D6 ,
c_evt.D7 ,
c_evt.D8 ,
c_evt.D9 ,
c_evt.D10 ,
c_evt.D11 ,
c_evt.D12 ,
c_evt.L1 ,
c_evt.L2 ,
c_evt.L3 ,
c_evt.L4 ,
c_evt.L5 ,
c_evt.L6 ,
c_evt.L7 ,
c_evt.L8 ,
c_evt.L9 ,
c_evt.L10 ,
c_evt.L11 ,
c_evt.L12 ,
v_TOVAGLIATO(1),
v_TOVAGLIATO(2),
v_TOVAGLIATO(3),
v_TOVAGLIATO(4),
v_TOVAGLIATO(5),
v_TOVAGLIATO(6),
v_TOVAGLIATO(7),
v_TOVAGLIATO(8),
v_TOVAGLIATO(9),
v_TOVAGLIATO(10),
v_TOVAGLIATO(11),
v_TOVAGLIATO(12),
c_evt.C1 ,
c_evt.C2 ,
c_evt.C3 ,
c_evt.C4 ,
c_evt.C5 ,
c_evt.C6 ,
c_evt.C7 ,
c_evt.C8 ,
c_evt.C9 ,
c_evt.C10 ,
c_evt.C11 ,
c_evt.C12 ,
v_TOVAGLIOLO(1),
v_TOVAGLIOLO(2),
v_TOVAGLIOLO(3),
v_TOVAGLIOLO(4),
v_TOVAGLIOLO(5),
v_TOVAGLIOLO(6),
v_TOVAGLIOLO(7),
v_TOVAGLIOLO(8),
v_TOVAGLIOLO(9),
v_TOVAGLIOLO(10),
v_TOVAGLIOLO(11),
v_TOVAGLIOLO(12),
v_AN_GELATO(1) || ', ' || v_AN_GELATO2(1),
v_AN_GELATO(2) || ', ' || v_AN_GELATO2(2),
v_AN_GELATO(3) || ', ' || v_AN_GELATO2(3),
v_AN_GELATO(4) || ', ' || v_AN_GELATO2(4),
v_AN_GELATO(5) || ', ' || v_AN_GELATO2(5),
v_AN_GELATO(6) || ', ' || v_AN_GELATO2(6),
v_AN_GELATO(7) || ', ' || v_AN_GELATO2(7),
v_AN_GELATO(8) || ', ' || v_AN_GELATO2(8),
v_AN_GELATO(9) || ', ' || v_AN_GELATO2(9),
v_AN_GELATO(10) || ', ' || v_AN_GELATO2(10),
v_AN_GELATO(11) || ', ' || v_AN_GELATO2(11),
v_AN_GELATO(12) || ', ' || v_AN_GELATO2(12),
v_AN_OPENBAR(1),
v_AN_OPENBAR(2),
v_AN_OPENBAR(3),
v_AN_OPENBAR(4),
v_AN_OPENBAR(5),
v_AN_OPENBAR(6),
v_AN_OPENBAR(7),
v_AN_OPENBAR(8),
v_AN_OPENBAR(9),
v_AN_OPENBAR(10),
v_AN_OPENBAR(11),
v_AN_OPENBAR(12),
v_AN_RUM(1),
v_AN_RUM(2),
v_AN_RUM(3),
v_AN_RUM(4),
v_AN_RUM(5),
v_AN_RUM(6),
v_AN_RUM(7),
v_AN_RUM(8),
v_AN_RUM(9),
v_AN_RUM(10),
v_AN_RUM(11),
v_AN_RUM(12),
c_evt.T1 ,
c_evt.T2 ,
c_evt.T3 ,
c_evt.T4 ,
c_evt.T5 ,
c_evt.T6 ,
c_evt.T7 ,
c_evt.T8 ,
c_evt.T9 ,
c_evt.T10 ,
c_evt.T11 ,
c_evt.T12 ,
c_evt.SM1 ,
c_evt.SM2 ,
c_evt.SM3 ,
c_evt.SM4 ,
c_evt.SM5 ,
c_evt.SM6 ,
c_evt.SM7 ,
c_evt.SM8 ,
c_evt.SM9 ,
c_evt.SM10 ,
c_evt.SM11 ,
c_evt.SM12
));
close c_evento;
RETURN;
END;```

View File

@@ -0,0 +1,245 @@
# F_REP_CUCINA
## Codice Sorgente
```sql
FUNCTION "F_REP_CUCINA" (p_data_in IN varchar2,p_data_fi IN varchar2)
RETURN t_rep_cucina_tab PIPELINED AS
cursor c_evento is
select e.id,
e.data,
l.LOCATION,
e.cliente,
t.DESCRIZIONE,
e.ORA_CERIMONIA,
e.ORA_EVENTO,
e.ALLERGIE,
e.torta,
e.CONFETTATA,
e.STAMPA_MENU,
e.extra_info,
e.cliente_email,
e.cliente_tel,
e.referente_tel,
e.distanza_location,
e.buffet_iniziale,
e.buffet_finale,
e.primi,
e.secondi,
e.vini,
e.extra_costi
from eventi e
left join location l on e.id_location = l.id
join tb_tipi_evento t on e.cod_tipo = t.cod_tipo
where e.data between to_date(p_data_in,'YYYYMMDD') and to_date(p_data_fi,'YYYYMMDD')
and e.stato = 300 -- Considero soltanto le schede confermate
and e.disabled = 0
and e.deleted = 0;
v_data date;
v_LOCATION varchar2(4000);
v_cliente varchar2(4000);
v_DESCRIZIONE varchar2(4000);
v_ORA_CERIMONIA varchar2(4000);
v_ORA_EVENTO varchar2(4000);
v_TOT_ADULTI number;
v_TOT_KINDER number;
v_TOT_BABY number;
v_TOT_STAFF number;
v_TOT_INVITATI number;
v_ALLERGIE varchar2(4000);
v_TORTA varchar2(4000);
v_CONFETTATA varchar2(4000);
v_STAMPA_MENU varchar2(4000);
v_angoli varchar2(4000);
v_EXTRA_INFO varchar2(4000);
v_NOTE_ADULTI varchar2(4000);
v_NOTE_KINDER varchar2(4000);
v_NOTE_BABY varchar2(4000);
v_NOTE_STAFF varchar2(4000);
v_cliente_email varchar2(4000) := '';
v_cliente_tel varchar2(4000) := '';
v_referente_tel varchar2(4000) := '';
v_distanza_location varchar2(4000) := '';
v_buffet_iniziale varchar2(4000) := '';
v_buffet_finale varchar2(4000) := '';
v_primi varchar2(4000) := '';
v_secondi varchar2(4000) := '';
v_vini varchar2(4000) := '';
v_extra_costi varchar2(4000) := '';
v_event_id number;
BEGIN
for c in c_evento
loop
begin
v_TOT_ADULTI := 0;
v_TOT_KINDER := 0;
v_TOT_BABY := 0;
v_TOT_STAFF := 0;
v_TOT_INVITATI := 0;
v_angoli := 0;
v_NOTE_ADULTI := '';
v_NOTE_KINDER := '';
v_NOTE_BABY := '';
v_NOTE_STAFF := '';
v_cliente_email := c.cliente_email;
v_cliente_tel := c.cliente_tel;
v_referente_tel := c.referente_tel;
v_distanza_location := c.distanza_location;
v_buffet_iniziale := c.buffet_iniziale;
v_buffet_finale := c.buffet_finale;
v_primi := c.primi;
v_secondi := c.secondi;
v_vini := c.vini;
v_extra_costi := c.extra_costi;
v_event_id := c.id;
--ospiti
select nvl(sum(o.NUMERO),0)
into v_TOT_ADULTI
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
--join tb_tipi_ospiti tos on o.cod_tipo_ospite = tos.COD_TIPO
where e.id = c.id
and o.cod_tipo_ospite = 8; -- adulti
begin
select o.NOTE
into v_NOTE_ADULTI
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
where e.id = c.id
and o.cod_tipo_ospite = 8; -- adulti
exception when no_data_found then null;
end;
select nvl(sum(o.NUMERO),0)
into v_TOT_KINDER
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
--join tb_tipi_ospiti tos on o.cod_tipo_ospite = tos.COD_TIPO
where e.id = c.id
and o.cod_tipo_ospite = 5; --Kinder
begin
select o.NOTE
into v_NOTE_KINDER
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
where e.id = c.id
and o.cod_tipo_ospite = 5; --Kinder
exception when no_data_found then null;
end;
select nvl(sum(o.NUMERO),0)
into v_TOT_BABY
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
--join tb_tipi_ospiti tos on o.cod_tipo_ospite = tos.COD_TIPO
where e.id = c.id
and o.cod_tipo_ospite = 6; -- Baby
begin
select o.NOTE
into v_NOTE_BABY
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
where e.id = c.id
and o.cod_tipo_ospite = 6; -- Baby
exception when no_data_found then null;
end;
select nvl(sum(o.NUMERO),0)
into v_TOT_STAFF
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
--join tb_tipi_ospiti tos on o.cod_tipo_ospite = tos.COD_TIPO
where e.id = c.id
and o.cod_tipo_ospite = 7; -- Staff
begin
select o.NOTE
into v_NOTE_STAFF
from eventi e
join eventi_det_ospiti o on e.id = o.id_evento
where e.id = c.id
and o.cod_tipo_ospite = 7; -- Staff
exception when no_data_found then null;
end;
select nvl(e.tot_ospiti,0)
into v_TOT_INVITATI
from eventi e
where e.id = c.id;
--angoli "speciali"
v_angoli := '';
for a in ( select trim(substr(a.descrizione || ' - ' || p.note,1,1000)) descrizione from eventi e
join eventi_det_prel p on e.id=p.id_evento
join articoli a on p.cod_articolo=a.cod_articolo
where a.flg_cucina = 1
and e.id = c.id
)
loop
v_angoli := a.descrizione || ', ' || v_angoli ;
end loop;
PIPE ROW(t_rep_cucina_row( c.data,c.location,c.cliente,
c.DESCRIZIONE,
to_char(c.ORA_CERIMONIA,'HH24:MI'),
to_char(c.ORA_EVENTO,'HH24:MI'),
v_TOT_ADULTI ,
v_TOT_KINDER ,
v_TOT_BABY ,
v_TOT_STAFF ,
v_TOT_INVITATI ,
c.ALLERGIE ,
c.TORTA ,
c.CONFETTATA ,
c.STAMPA_MENU ,
v_angoli,
c.extra_info ,
v_NOTE_ADULTI ,
v_NOTE_KINDER ,
v_NOTE_BABY ,
v_NOTE_STAFF ,
v_cliente_email ,
v_cliente_tel ,
v_referente_tel ,
v_distanza_location ,
v_buffet_iniziale ,
v_buffet_finale ,
v_primi ,
v_secondi ,
v_vini ,
v_extra_costi ,
v_event_id
));
--exception when others
-- then null;
exception when others
then
RAISE_APPLICATION_ERROR(-20000, 'Errore: ' || SQLCODE || ' - ' || SUBSTR(SQLERRM, 1 , 64));
end;
end loop;
RETURN;
END;```

View File

@@ -0,0 +1,20 @@
# F_USER_IN_ROLE
## Codice Sorgente
```sql
FUNCTION F_USER_IN_ROLE
(
P_USER IN VARCHAR2
, P_ROLE IN VARCHAR2
) RETURN NUMBER AS
v_has_role number := 0;
BEGIN
select count(column_value)
into v_has_role
from tb_config, table(split(strvalue, ':'))
where upper(name) = upper(P_ROLE)
and upper(column_value) = upper(P_USER);
return case when v_has_role > 0 then 1 else 0 end;
END F_USER_IN_ROLE;```

View File

@@ -0,0 +1,15 @@
# F_USER_IN_ROLE_STR
## Codice Sorgente
```sql
FUNCTION F_USER_IN_ROLE_STR
(
P_USER IN VARCHAR2
, P_ROLE IN VARCHAR2
) RETURN VARCHAR2 AS
v_has_role number := 0;
BEGIN
return case when F_USER_IN_ROLE(P_USER, P_ROLE) > 0 then 'true' else 'false' end;
END F_USER_IN_ROLE_STR;
```

View File

@@ -0,0 +1,19 @@
# GET_PARAM_VALUE
## Codice Sorgente
```sql
function get_param_value(p_name varchar2)
return varchar2 as
v_result varchar2(255);
begin
select strvalue
into v_result
from tb_config
where upper(name) = upper(p_name);
return v_result;
exception when others then
return null;
end;
```

View File

@@ -0,0 +1,36 @@
# MY_INSTR
## Codice Sorgente
```sql
function my_instr( p_value varchar2,
p_delim varchar2 )
return number
as
i number;
l_length number;
begin
if p_value is null then
return null;
end if;
i := 1;
l_length := length(p_value);
for i in 1..l_length
loop
if substr(p_value, i, length(p_delim)) = p_delim then
return i;
end if;
end loop;
return 0;
end;
```

127
docs/functions/README.md Normal file
View File

@@ -0,0 +1,127 @@
# Funzioni Database
Questa cartella contiene la documentazione di tutte le 23 funzioni del database.
## Funzioni Calcolo Quantità e Disponibilità
| Funzione | Ritorno | Descrizione |
|----------|---------|-------------|
| [F_GET_QTA_IMPEGNATA](F_GET_QTA_IMPEGNATA.md) | NUMBER | Quantità impegnata di un articolo in un range di date |
| [F_GET_TOT_OSPITI](F_GET_TOT_OSPITI.md) | NUMBER | Totale ospiti per evento (opzionale per tipo) |
| [F_GET_OSPITI](F_GET_OSPITI.md) | TABLE | Dettaglio ospiti per evento (funzione pipelined) |
| [F_LIST_PRELIEVO_ADD_ARTICOLO](F_LIST_PRELIEVO_ADD_ARTICOLO.md) | VARCHAR2 (JSON) | Aggiunge articolo alla lista prelievo con validazioni |
## Funzioni Calcolo Costi
| Funzione | Ritorno | Descrizione |
|----------|---------|-------------|
| [F_GET_COSTO_ARTICOLO](F_GET_COSTO_ARTICOLO.md) | NUMBER | Costo unitario articolo a una data specifica |
## Funzioni Validazione
| Funzione | Ritorno | Descrizione |
|----------|---------|-------------|
| [F_EVENTO_SCADUTO](F_EVENTO_SCADUTO.md) | NUMBER (0/1) | Verifica se preventivo è scaduto |
| [F_MAX_NUMERO_EVENTI_RAGGIUNTO](F_MAX_NUMERO_EVENTI_RAGGIUNTO.md) | NUMBER (-1/0/1) | Verifica limite eventi giornaliero |
| [F_MAX_NUM_EVENTI_CONFERMATI](F_MAX_NUM_EVENTI_CONFERMATI.md) | NUMBER (0/1) | Verifica limite eventi confermati |
| [F_CI_SONO_EVENTI_CONFERMATI](F_CI_SONO_EVENTI_CONFERMATI.md) | NUMBER (0/1) | Check esistenza eventi confermati in data/location |
## Funzioni Report
| Funzione | Ritorno | Descrizione |
|----------|---------|-------------|
| [F_REP_ALLESTIMENTI](F_REP_ALLESTIMENTI.md) | TABLE | Dati per report allestimenti (pipelined) |
| [F_REP_CUCINA](F_REP_CUCINA.md) | TABLE | Dati per report cucina (pipelined) |
| [F_GET_ANGOLO_ALLESTIMENTO](F_GET_ANGOLO_ALLESTIMENTO.md) | VARCHAR2 | Descrizione angolo allestimento |
| [F_GET_ANGOLO_ALLESTIMENTO_OB](F_GET_ANGOLO_ALLESTIMENTO_OB.md) | VARCHAR2 | Descrizione angolo open bar |
| [F_GET_TOVAGLIATO_ALLESTIMENTO](F_GET_TOVAGLIATO_ALLESTIMENTO.md) | VARCHAR2 | Descrizione tovagliato |
## Funzioni Autorizzazione
| Funzione | Ritorno | Descrizione |
|----------|---------|-------------|
| [F_USER_IN_ROLE](F_USER_IN_ROLE.md) | NUMBER (0/1) | Verifica appartenenza utente a ruolo |
| [F_USER_IN_ROLE_STR](F_USER_IN_ROLE_STR.md) | VARCHAR2 | Verifica ruolo (ritorna stringa) |
## Funzioni Utility
| Funzione | Ritorno | Descrizione |
|----------|---------|-------------|
| [F_DAY_TO_NAME](F_DAY_TO_NAME.md) | VARCHAR2 | Converte numero giorno in nome italiano |
| [STRING_TO_TABLE_ENUM](STRING_TO_TABLE_ENUM.md) | TABLE | Converte stringa delimitata in tabella |
| [GET_PARAM_VALUE](GET_PARAM_VALUE.md) | VARCHAR2 | Recupera valore parametro da TB_CONFIG |
| [SPLIT](SPLIT.md) | TABLE | Split stringa in elementi |
| [MY_INSTR](MY_INSTR.md) | NUMBER | Funzione INSTR personalizzata |
| [CLOB2BLOB](CLOB2BLOB.md) | BLOB | Conversione CLOB → BLOB |
| [EXTDATE_GET_ITA](EXTDATE_GET_ITA.md) | VARCHAR2 | Formatta data in italiano esteso |
## Dettaglio Funzioni Critiche
### F_GET_QTA_IMPEGNATA
```sql
FUNCTION F_GET_QTA_IMPEGNATA(
p_codart VARCHAR2,
p_data_from DATE,
p_data_to DATE DEFAULT NULL
) RETURN NUMBER
```
**Logica:**
- Interroga `V_IMPEGNI_ARTICOLI`
- Somma quantità impegnate nel range di date
- Se `p_data_to` è NULL, usa `p_data_from`
### F_EVENT0_SCADUTO
```sql
FUNCTION F_EVENTO_SCADUTO(
DATA_SCADENZA IN DATE,
STATO_EVENTO IN NUMBER,
STATO_FROM IN NUMBER,
STATO_TO IN NUMBER
) RETURN NUMBER
```
**Logica:**
- Ritorna 1 se `TRUNC(DATA_SCADENZA) <= TRUNC(SYSDATE)`
AND `STATO_EVENTO BETWEEN STATO_FROM AND STATO_TO`
- Altrimenti ritorna 0
### F_LIST_PRELIEVO_ADD_ARTICOLO
```sql
FUNCTION F_LIST_PRELIEVO_ADD_ARTICOLO(
p_event_id NUMBER,
p_articolo_add VARCHAR2,
p_qta_aperitivo NUMBER := 0,
p_qta_seduto NUMBER := 0,
p_qta_dolci NUMBER := 0
) RETURN VARCHAR2
```
**Logica:**
1. Verifica esistenza evento
2. Verifica esistenza articolo
3. Recupera giacenza articolo
4. Controlla impegni in date vicine (±2 giorni)
5. Inserisce record in `EVENTI_DET_PREL`
6. Chiama `EVENTI_AGGIORNA_QTA_LISTA`
7. Ritorna JSON con esito:
- `{"type":"success","message":"..."}`
- `{"type":"warning","message":"..."}`
- `{"type":"error","code":"...","stack":"...","message":"..."}`
### F_GET_COSTO_ARTICOLO
```sql
FUNCTION F_GET_COSTO_ARTICOLO(
p_cod_articolo VARCHAR2,
p_date DATE
) RETURN NUMBER
```
**Logica:**
1. Cerca costo in `COSTI_ARTICOLI` alla data esatta
2. Se non trovato, prende ultimo costo disponibile
3. Se non trovato nulla, ritorna 0

46
docs/functions/SPLIT.md Normal file
View File

@@ -0,0 +1,46 @@
# SPLIT
## Codice Sorgente
```sql
FUNCTION "SPLIT"
(
p_list varchar2,
p_del varchar2 := ','
) return string_list
is
l_idx integer;
l_list varchar2(32767) := p_list;
l_value varchar2(32767);
l_retval string_list;
begin
l_retval := string_list();
loop
--l_idx := instr(l_list,p_del);
l_idx := my_instr(l_list,p_del);
l_retval.extend;
if l_idx > 0 then
--pipe row(substr(l_list,1,l_idx-1));
--l_list := substr(l_list,l_idx+length(p_del));
l_retval(l_retval.count) := substr(l_list, 1, l_idx - 1);
l_list := substr(l_list,l_idx+length(p_del));
else
--pipe row(l_list);
--exit;
l_retval(l_retval.count) := l_list;
exit;
end if;
end loop;
return l_retval;
end split;
```

View File

@@ -0,0 +1,26 @@
# STRING_TO_TABLE_ENUM
## Codice Sorgente
```sql
FUNCTION "STRING_TO_TABLE_ENUM" (p_string VARCHAR2, v_level in number default 0, p_separator varchar2 default ':') RETURN ENUM_TABLE_TYPE pipelined IS
conta number := 0;
BEGIN
FOR c IN ( select ROW_NUMBER() OVER (ORDER BY ROWNUM) id, result
from (
with test as (
select p_string col
from dual
)
select nvl(regexp_substr(col, '[^' || p_separator || ']+', 1, level), '') result
from test
connect by level <= length(regexp_replace(col, '[^' || p_separator || ']+')) + 1)
)
LOOP
conta := conta + 1;
if v_level = 0 or ( v_level = conta ) then
pipe row(enum_table_object(c.id,c.result));
end if;
END LOOP;
RETURN;
END STRING_TO_TABLE_ENUM;```

422
docs/index.md Normal file
View File

@@ -0,0 +1,422 @@
# Apollinare Catering - Documentazione Completa
Questa documentazione contiene l'estrazione completa di tutti gli oggetti del database Oracle e dell'applicazione APEX di Apollinare Catering & Banqueting.
## [Application Overview](APPLICATION_OVERVIEW.md)
**Apollinare Catering & Banqueting Management Software** è un gestionale completo per aziende di catering che gestisce l'intero ciclo di vita di un evento: dalla richiesta del cliente, al preventivo, alla conferma, fino all'esecuzione.
### Funzionalità Principali
| Area | Descrizione |
| --------------------- | ------------------------------------------- |
| **Gestione Eventi** | Creazione, workflow stati, versioning |
| **Gestione Ospiti** | Tipologie ospiti, conteggi, coefficienti |
| **Lista Prelievo** | Calcolo automatico quantità materiale |
| **Risorse/Staff** | Pianificazione personale per evento |
| **Acconti/Pagamenti** | Sistema caparre 30%-50%-20%, solleciti |
| **Calendario** | Vista eventi, limiti giornalieri, conflitti |
| **Reporting** | Schede evento, preventivi, report cucina |
### Proposta SaaS: CaterPro
La documentazione include una proposta per trasformare Apollinare in **CaterPro**, una piattaforma SaaS multi-tenant:
- **Target**: Piccole, medie e grandi aziende di catering
- **Stack**: .NET 8 + React TypeScript + PostgreSQL/Oracle
- **Pricing**: Da €49/mese (Basic) a €399/mese (Enterprise)
- **Roadmap**: 10-14 mesi per feature parity + SaaS
Leggi la [documentazione completa](APPLICATION_OVERVIEW.md) per dettagli su architettura, funzionalità e roadmap.
---
## Struttura della Documentazione
```
docs/
├── apex/ # Applicazione APEX
│ ├── README.md # Overview applicazione
│ ├── pages/ # 56 pagine
│ ├── processes/ # 98 processi
│ ├── lovs/ # 12 List of Values
│ ├── javascript/ # Librerie JavaScript
│ ├── authorization/ # 5 schemi autorizzazione
│ ├── dynamic-actions/ # Azioni dinamiche
│ ├── items/ # Items condivisi
│ ├── regions/ # Regioni condivise
│ └── navigation/ # Navigazione
├── tables/ # 32 tabelle
├── views/ # 26 viste
├── procedures/ # 11 stored procedures
├── functions/ # 23 funzioni
├── packages/ # 17 packages
├── triggers/ # 19 triggers
├── sequences/ # 22 sequences
└── types/ # 10 tipi custom
```
---
## APEX Application Documentation
### [APEX Application Overview](apex/README.md)
**Application:** APCB Project (ID: 112)
**APEX Version:** 21.1.0
**Schema:** APOLLINARECATERINGPROD
| Component | Count |
| --------------- | ----- |
| Pages | 56 |
| Items | 302 |
| Processes | 98 |
| Regions | 151 |
| Buttons | 119 |
| Dynamic Actions | 62 |
| LOVs | 12 |
### Key APEX Documentation
- [APEX README](apex/README.md) - Application overview and navigation
- [Processes Documentation](apex/processes/README.md) - All 98 processes with PL/SQL code
- [LOVs Documentation](apex/lovs/README.md) - 12 List of Values definitions
- [JavaScript Libraries](apex/javascript/README.md) - Custom ajaxUtils.js and iframeObj.js
- [Authorization Schemes](apex/authorization/README.md) - 5 security schemes
### Critical APEX Pages
| Page | Name | Description |
| ------ | ---------------- | ------------------------------------------------- |
| 1 | Home | Dashboard principale |
| **22** | **Nuovo Evento** | **Pagina più complessa (108 items, 32 processi)** |
| 9 | Liste | Lista eventi |
| 12 | Calendario | Calendario eventi |
| 35 | Schede | Schede evento |
---
## Indice per Categoria
### [Tabelle](tables/README.md) (32)
Tabelle principali del dominio business:
- [EVENTI](tables/EVENTI.md) - Tabella principale eventi
- [EVENTI_DET_PREL](tables/EVENTI_DET_PREL.md) - Liste prelievo
- [EVENTI_DET_OSPITI](tables/EVENTI_DET_OSPITI.md) - Dettaglio ospiti
- [EVENTI_DET_RIS](tables/EVENTI_DET_RIS.md) - Risorse assegnate
- [EVENTI_DET_DEGUST](tables/EVENTI_DET_DEGUST.md) - Degustazioni
- [EVENTI_ACCONTI](tables/EVENTI_ACCONTI.md) - Gestione acconti/pagamenti
- [EVENTI_ALTRICOSTI](tables/EVENTI_ALTRICOSTI.md) - Altri costi
- [EVENTI_ALLEG](tables/EVENTI_ALLEG.md) - Allegati
- [ARTICOLI](tables/ARTICOLI.md) - Catalogo articoli
- [COSTI_ARTICOLI](tables/COSTI_ARTICOLI.md) - Storico costi
- [CLIENTI](tables/CLIENTI.md) - Anagrafica clienti
- [LOCATION](tables/LOCATION.md) - Location eventi
- [RISORSE](tables/RISORSE.md) - Personale
Tabelle di lookup:
- [TB_TIPI_MAT](tables/TB_TIPI_MAT.md) - Tipi materiale
- [TB_CODICI_CATEG](tables/TB_CODICI_CATEG.md) - Categorie
- [TB_TIPI_EVENTO](tables/TB_TIPI_EVENTO.md) - Tipi evento
- [TB_TIPI_OSPITI](tables/TB_TIPI_OSPITI.md) - Tipi ospiti
- [TB_TIPI_RISORSA](tables/TB_TIPI_RISORSA.md) - Tipi risorsa
- [TB_TIPI_PASTO](tables/TB_TIPI_PASTO.md) - Tipi pasto
- [TB_CALENDAR_LOCKS](tables/TB_CALENDAR_LOCKS.md) - Limiti calendario
- [TB_CONFIG](tables/TB_CONFIG.md) - Configurazioni
Tabelle di sistema:
- [USERS_READONLY](tables/USERS_READONLY.md) - Permessi utenti
- [XLIB_LOGS](tables/XLIB_LOGS.md) - Log applicazione
- [XLIB_COMPONENTS](tables/XLIB_COMPONENTS.md) - Componenti
- [XLIB_JASPERREPORTS_CONF](tables/XLIB_JASPERREPORTS_CONF.md) - Config report
- [XLIB_JASPERREPORTS_DEMOS](tables/XLIB_JASPERREPORTS_DEMOS.md) - Demo report
### [Viste](views/README.md) (26)
Viste per calcolo costi:
- [GET_COSTO_ART_BY_EVT](views/GET_COSTO_ART_BY_EVT.md) - Costo articoli per evento
- [GET_COSTO_ART_EVT](views/GET_COSTO_ART_EVT.md) - Costo articoli aggregato
- [GET_COSTO_CATEG_EVT](views/GET_COSTO_CATEG_EVT.md) - Costo per categoria
- [GET_COSTO_DEGUS_EVT](views/GET_COSTO_DEGUS_EVT.md) - Costo degustazioni
- [GET_COSTO_OSPITI_EVT](views/GET_COSTO_OSPITI_EVT.md) - Costo ospiti
- [GET_COSTO_RIS_EVT](views/GET_COSTO_RIS_EVT.md) - Costo risorse
- [GET_COSTO_TIPI_EVT](views/GET_COSTO_TIPI_EVT.md) - Costo per tipo
- [GET_ULTIMI_COSTI](views/GET_ULTIMI_COSTI.md) - Ultimi costi articoli
Viste per eventi:
- [GET_EVT_DATA](views/GET_EVT_DATA.md) - Dati evento completi
- [GET_EVT_DATA_PRINT](views/GET_EVT_DATA_PRINT.md) - Dati per stampa
- [GET_PREL_ART_TOT](views/GET_PREL_ART_TOT.md) - Totali prelievo
- [GET_PREL_BY_EVT](views/GET_PREL_BY_EVT.md) - Prelievi per evento
Viste per calendario e stato:
- [VW_CALENDARIO_EVENTI](views/VW_CALENDARIO_EVENTI.md) - Vista calendario
- [VW_EVENT_COLOR](views/VW_EVENT_COLOR.md) - Colori stati
- [VW_EVENTI_STATUSES](views/VW_EVENTI_STATUSES.md) - Stati eventi
Viste per giacenze:
- [V_IMPEGNI_ARTICOLI](views/V_IMPEGNI_ARTICOLI.md) - Impegni articoli
- [V_IMPEGNI_ARTICOLI_LOC](views/V_IMPEGNI_ARTICOLI_LOC.md) - Impegni per location
Viste per report:
- [V_REP_ALLESTIMENTI](views/V_REP_ALLESTIMENTI.md) - Report allestimenti
- [VW_REP_DEGUSTAZIONI](views/VW_REP_DEGUSTAZIONI.md) - Report degustazioni
- [V_GRIGLIA](views/V_GRIGLIA.md) - Vista griglia
- [GET_REPORT_CONSUNTIVO_PER_DATA](views/GET_REPORT_CONSUNTIVO_PER_DATA.md) - Consuntivo
Viste per utenti/permessi:
- [GET_CONSUNTIVI_USERS](views/GET_CONSUNTIVI_USERS.md) - Utenti consuntivi
- [GET_GESTORI_USERS](views/GET_GESTORI_USERS.md) - Utenti gestori
- [GET_USERS_LIST](views/GET_USERS_LIST.md) - Lista utenti
Viste per pagamenti:
- [GET_EVENTI_DA_PAGARE_ENTRO_65GG](views/GET_EVENTI_DA_PAGARE_ENTRO_65GG.md) - Eventi da sollecitare
### [Stored Procedures](procedures/README.md) (11)
Business logic principale:
- [EVENTI_AGGIORNA_QTA_LISTA](procedures/EVENTI_AGGIORNA_QTA_LISTA.md) - Ricalcolo quantità lista prelievo
- [EVENTI_AGGIORNA_TOT_OSPITI](procedures/EVENTI_AGGIORNA_TOT_OSPITI.md) - Aggiorna totale ospiti
- [EVENTI_COPIA](procedures/EVENTI_COPIA.md) - Duplicazione evento
- [EVENTI_RICALCOLA_ACCONTI](procedures/EVENTI_RICALCOLA_ACCONTI.md) - Ricalcolo acconti
- [EVENTO_ELIMINA_PRELIEVI](procedures/EVENTO_ELIMINA_PRELIEVI.md) - Elimina prelievi
- [LISTE_COPIA](procedures/LISTE_COPIA.md) - Copia liste tra eventi
- [P_CANCEL_SAME_LOCATION_EVENTS](procedures/P_CANCEL_SAME_LOCATION_EVENTS.md) - Annulla eventi stessa location
Utility:
- [ROWSORT_TIPI](procedures/ROWSORT_TIPI.md) - Ordinamento tipi
- [HTPPRN](procedures/HTPPRN.md) - Stampa HTTP
- [SEND_DATA_TO_DROPBOX](procedures/SEND_DATA_TO_DROPBOX.md) - Export Dropbox
- [XLOG](procedures/XLOG.md) - Logging
### [Funzioni](functions/README.md) (23)
Calcolo quantità e disponibilità:
- [F_GET_QTA_IMPEGNATA](functions/F_GET_QTA_IMPEGNATA.md) - Quantità impegnata
- [F_GET_TOT_OSPITI](functions/F_GET_TOT_OSPITI.md) - Totale ospiti
- [F_GET_OSPITI](functions/F_GET_OSPITI.md) - Dettaglio ospiti (pipelined)
- [F_LIST_PRELIEVO_ADD_ARTICOLO](functions/F_LIST_PRELIEVO_ADD_ARTICOLO.md) - Aggiunta articolo
Calcolo costi:
- [F_GET_COSTO_ARTICOLO](functions/F_GET_COSTO_ARTICOLO.md) - Costo articolo a data
Validazioni:
- [F_EVENTO_SCADUTO](functions/F_EVENTO_SCADUTO.md) - Verifica scadenza
- [F_MAX_NUMERO_EVENTI_RAGGIUNTO](functions/F_MAX_NUMERO_EVENTI_RAGGIUNTO.md) - Limite eventi
- [F_MAX_NUM_EVENTI_CONFERMATI](functions/F_MAX_NUM_EVENTI_CONFERMATI.md) - Limite confermati
- [F_CI_SONO_EVENTI_CONFERMATI](functions/F_CI_SONO_EVENTI_CONFERMATI.md) - Check confermati
Report:
- [F_REP_ALLESTIMENTI](functions/F_REP_ALLESTIMENTI.md) - Report allestimenti
- [F_REP_CUCINA](functions/F_REP_CUCINA.md) - Report cucina
- [F_GET_ANGOLO_ALLESTIMENTO](functions/F_GET_ANGOLO_ALLESTIMENTO.md) - Angolo allestimento
- [F_GET_ANGOLO_ALLESTIMENTO_OB](functions/F_GET_ANGOLO_ALLESTIMENTO_OB.md) - Angolo open bar
- [F_GET_TOVAGLIATO_ALLESTIMENTO](functions/F_GET_TOVAGLIATO_ALLESTIMENTO.md) - Tovagliato
Autorizzazioni:
- [F_USER_IN_ROLE](functions/F_USER_IN_ROLE.md) - Verifica ruolo utente
- [F_USER_IN_ROLE_STR](functions/F_USER_IN_ROLE_STR.md) - Ruolo utente (stringa)
Utility:
- [F_DAY_TO_NAME](functions/F_DAY_TO_NAME.md) - Giorno in italiano
- [STRING_TO_TABLE_ENUM](functions/STRING_TO_TABLE_ENUM.md) - Stringa a tabella
- [GET_PARAM_VALUE](functions/GET_PARAM_VALUE.md) - Valore parametro
- [SPLIT](functions/SPLIT.md) - Split stringa
- [MY_INSTR](functions/MY_INSTR.md) - Instr custom
- [CLOB2BLOB](functions/CLOB2BLOB.md) - Conversione CLOB
- [EXTDATE_GET_ITA](functions/EXTDATE_GET_ITA.md) - Data in italiano
### [Packages](packages/README.md) (17)
Business:
- [MAIL_PKG](packages/MAIL_PKG.md) - Gestione invio email automatiche
Utility esterne:
- [UTL_BASE64](packages/UTL_BASE64.md) - Encoding Base64
JasperReports:
- [XLIB_JASPERREPORTS](packages/XLIB_JASPERREPORTS.md) - Integrazione JasperReports
- [XLIB_JASPERREPORTS_IMG](packages/XLIB_JASPERREPORTS_IMG.md) - Immagini report
HTTP/Componenti:
- [XLIB_HTTP](packages/XLIB_HTTP.md) - Chiamate HTTP
- [XLIB_COMPONENT](packages/XLIB_COMPONENT.md) - Componenti
- [XLIB_LOG](packages/XLIB_LOG.md) - Logging
JSON (libreria PLJSON):
- [PLJSON_DYN](packages/PLJSON_DYN.md)
- [PLJSON_EXT](packages/PLJSON_EXT.md)
- [PLJSON_HELPER](packages/PLJSON_HELPER.md)
- [PLJSON_ML](packages/PLJSON_ML.md)
- [PLJSON_OBJECT_CACHE](packages/PLJSON_OBJECT_CACHE.md)
- [PLJSON_PARSER](packages/PLJSON_PARSER.md)
- [PLJSON_PRINTER](packages/PLJSON_PRINTER.md)
- [PLJSON_UT](packages/PLJSON_UT.md)
- [PLJSON_UTIL_PKG](packages/PLJSON_UTIL_PKG.md)
- [PLJSON_XML](packages/PLJSON_XML.md)
### [Triggers](triggers/README.md) (19)
Generazione ID:
- [EVENTI_TRG](triggers/EVENTI_TRG.md) - ID eventi + inizializzazione
- [EVENTI_AI_TRG](triggers/EVENTI_AI_TRG.md) - Creazione ospiti default
- [EVENTI_DET_PREL_TRG](triggers/EVENTI_DET_PREL_TRG.md) - ID prelievi
- [EVENTI_DET_RIS_TRG](triggers/EVENTI_DET_RIS_TRG.md) - ID risorse
- [EVENTI_DET_DEGUST_TRG](triggers/EVENTI_DET_DEGUST_TRG.md) - ID degustazioni
- [EVENTI_ACCONTI_TRG](triggers/EVENTI_ACCONTI_TRG.md) - ID acconti
- [EVENTI_ALTRICOSTI_TRG](triggers/EVENTI_ALTRICOSTI_TRG.md) - ID altri costi
- [EVENTI_ALLEG_TRG](triggers/EVENTI_ALLEG_TRG.md) - ID allegati
- [CLIENTI_TRG](triggers/CLIENTI_TRG.md) - ID clienti
- [LOCATION_TRG](triggers/LOCATION_TRG.md) - ID location
- [RISORSE_TRG](triggers/RISORSE_TRG.md) - ID risorse
- [ARTICOLI_DET_REGOLE_TRG](triggers/ARTICOLI_DET_REGOLE_TRG.md) - ID regole articoli
- [TB_TIPI_PASTO_TRG](triggers/TB_TIPI_PASTO_TRG.md) - ID tipi pasto
Business logic:
- [EVENTI_DET_OSPITI_TRG_AI](triggers/EVENTI_DET_OSPITI_TRG_AI.md) - Aggiornamento ospiti
- [EVENTI_DET_PREL_QTA_TOT_TRG](triggers/EVENTI_DET_PREL_QTA_TOT_TRG.md) - Calcolo quantità totale
Ordinamento:
- [ADD_COD_STEP](triggers/ADD_COD_STEP.md) - Ordine tipi materiale
- [ON_DELETE_REORDER](triggers/ON_DELETE_REORDER.md) - Riordino dopo delete
Sistema:
- [BI_GL_SCHEMA_CHANGES](triggers/BI_GL_SCHEMA_CHANGES.md) - Log modifiche schema
- [XLIB_LOGS_BI_TRG](triggers/XLIB_LOGS_BI_TRG.md) - Log applicazione
### [Sequences](sequences/README.md) (22)
Tutte le sequence del database.
### [Types](types/README.md) (10)
Tipi custom:
- [T_DET_OSPITI_ROW](types/T_DET_OSPITI_ROW.md) / [T_DET_OSPITI_TAB](types/T_DET_OSPITI_TAB.md) - Tipo per F_GET_OSPITI
- [T_REP_ALLESTIMENTI_ROW](types/T_REP_ALLESTIMENTI_ROW.md) / [T_REP_ALLESTIMENTI_TAB](types/T_REP_ALLESTIMENTI_TAB.md) - Tipo per F_REP_ALLESTIMENTI
- [T_REP_CUCINA_ROW](types/T_REP_CUCINA_ROW.md) / [T_REP_CUCINA_TAB](types/T_REP_CUCINA_TAB.md) - Tipo per F_REP_CUCINA
- [STRING_LIST](types/STRING_LIST.md) - Lista stringhe
- [ENUM_TABLE_OBJECT](types/ENUM_TABLE_OBJECT.md) / [ENUM_TABLE_TYPE](types/ENUM_TABLE_TYPE.md) - Tipi per STRING_TO_TABLE_ENUM
- [XLIB_VC2_ARRAY_T](types/XLIB_VC2_ARRAY_T.md) - Array varchar2
---
## Schema ER Semplificato
```
┌─────────────┐
│ CLIENTI │
└──────┬──────┘
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ LOCATION │◄────│ EVENTI │────►│TB_TIPI_EVENTO│
└─────────────┘ └──────┬──────┘ └─────────────┘
┌─────────────────┼─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│EVENTI_DET_OSPITI│ │ EVENTI_DET_PREL │ │ EVENTI_DET_RIS │
└─────────────────┘ └────────┬────────┘ └────────┬────────┘
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ TB_TIPI_OSPITI │ │ ARTICOLI │ │ RISORSE │
└─────────────────┘ └────────┬────────┘ └─────────────────┘
┌─────────────────┐
│ TB_CODICI_CATEG │
└────────┬────────┘
┌─────────────────┐
│ TB_TIPI_MAT │
└─────────────────┘
┌─────────────────┬─────────────────┐
│ │ │
▼ ▼ ▼
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│EVENTI_DET_DEGUST│ │ EVENTI_ACCONTI │ │EVENTI_ALTRICOSTI│
└─────────────────┘ └─────────────────┘ └─────────────────┘
```
## Workflow Stati Evento
```
┌──────────────┐
│ PREVENTIVO │ (100) - Bianco
└──────┬───────┘
│ Degustazione
┌──────────────┐
│SCHEDA EVENTO │ (200) - Celeste
│(preparazione)│
└──────┬───────┘
│ Prima caparra
┌──────────────┐
│ SCHEDA │ (300) - Giallo
│ CONFERMATA │
└──────┬───────┘
│ Quasi confermato
┌──────────────┐
│SCHEDA QUASI │ (350) - Arancio
│ CONFERMATA │
└──────┬───────┘
│ Conferma definitiva
┌──────────────┐
│ CONFERMATO │ (400) - Verde
└──────────────┘
│ Rifiuto/Scadenza
┌──────────────┐
│NON ACCETTATO/│ (900) - Viola
│ SUPERATO │
└──────────────┘
```
## Note per lo Sviluppo
1. **Packages PLJSON\_\***: Libreria esterna per parsing JSON, può essere sostituita con funzionalità native .NET
2. **Packages XLIB\_\***: Componenti per integrazione JasperReports, da valutare sostituzione con report .NET
3. **Trigger per ID**: In .NET usare Identity columns o GUID
4. **Calcolo quantità**: La logica in `EVENTI_AGGIORNA_QTA_LISTA` è critica e deve essere portata fedelmente
5. **Sistema acconti**: Le percentuali 30%-50%-20% sono hardcoded, valutare parametrizzazione

392
docs/packages/MAIL_PKG.md Normal file
View File

@@ -0,0 +1,392 @@
# MAIL_PKG
## Package Specification
```sql
PACKAGE MAIL_PKG AS
--==================================================================================
-- SPECIFICA DEL PACKAGE
-- Aggiunta delle nuove procedure per il reminder della seconda caparra.
-- Ultima modifica: 26/07/2024
--==================================================================================
-- Procedura generica per inviare email
procedure send_custom_mail(p_recipients varchar2, p_subject varchar2, p_body varchar2);
-- Procedure esistenti
PROCEDURE send_richiesta_riscontro_preventivo (
p_style_id NUMBER DEFAULT 1,
p_evento_id NUMBER
);
PROCEDURE send_richiesta_riscontro_preventivo_job;
PROCEDURE send_richiesta_riscontro_post_degustazione (
p_style_id NUMBER DEFAULT 1,
p_evento_id NUMBER
);
PROCEDURE send_richiesta_riscontro_post_degustazione_job;
-- ===============================================================================
-- NUOVE PROCEDURE PER IL REMINDER DELLA SECONDA CAPARRA
-- Data creazione: 26/07/2024
-- ===============================================================================
/**
* @descr Costruisce e invia la mail di sollecito per la seconda caparra per un dato evento.
* @param p_style_id ID dello stile HTML da applicare alla mail.
* @param p_evento_id ID dell'evento per cui inviare la notifica.
*/
PROCEDURE send_reminder_seconda_caparra (
p_style_id NUMBER DEFAULT 1,
p_evento_id NUMBER
);
/**
* @descr Job che seleziona gli eventi per cui inviare il sollecito della seconda caparra.
* Il primo invio avviene 65 giorni prima dell'evento.
* Gli invii successivi avvengono ogni 5 giorni fino alla data dell'evento,
* se la seconda caparra non risulta ancora pagata.
*/
PROCEDURE send_reminder_seconda_caparra_job;
END MAIL_PKG;
```
## Package Body
```sql
PACKAGE BODY MAIL_PKG AS
--==================================================================================
-- BODY DEL PACKAGE
-- Implementazione delle nuove procedure e mantenimento di quelle esistenti.
-- Ultima modifica: 25/07/2024
--==================================================================================
/**
* @descr Procedura di utility generica per inviare una mail tramite APEX_MAIL.
* @param p_recipients Lista di destinatari separati da virgola.
* @param p_subject Oggetto della mail.
* @param p_body Corpo della mail (può contenere HTML).
*/
procedure send_custom_mail(p_recipients varchar2, p_subject varchar2, p_body varchar2) AS
BEGIN
-- Se non ci sono destinatari, interrompe l'esecuzione.
if trim(p_recipients) is null
then
return;
end if;
-- Utilizza il package APEX_MAIL per comporre e inviare l'email.
APEX_MAIL.SEND(
p_to => p_recipients,
p_from => 'noreply@apollinarecatering.it',
p_bcc => 'monia@apollinarecatering.it, matrimonio@apollinarecatering.it',
--p_bcc => 'monia@apollinarecatering.it, maria@apollinarecatering.it', -- Copia conoscenza nascosta
p_subj => p_subject,
p_body => p_body,
p_body_html => p_body -- Il corpo viene inviato sia come testo che come HTML.
);
-- Forza l'invio immediato delle mail presenti nella coda di APEX.
APEX_MAIL.PUSH_QUEUE;
END send_custom_mail;
/**
* @descr Invia una mail di sollecito per avere un riscontro su un preventivo inviato per eventi in stato preventivo (100) o scheda confermata (200).
* @param p_style_id ID dello stile HTML da usare per il corpo della mail.
* @param p_evento_id ID dell'evento a cui la mail fa riferimento.
*/
PROCEDURE send_richiesta_riscontro_preventivo (
p_style_id NUMBER DEFAULT 1,
p_evento_id NUMBER
) AS
v_evento eventi%ROWTYPE;
v_location location%ROWTYPE;
BEGIN
-- Imposta la lingua e il formato della data della sessione per garantire la corretta formattazione.
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_LANGUAGE="ITALIAN"';
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_DATE_FORMAT="DD-MON-YYYY"';
-- Recupera i dettagli dell'evento e della location.
BEGIN
-- Seleziona i dati dell'evento solo se le mail sono abilitate (mail_enabled > 0).
SELECT e.* INTO v_evento
FROM eventi e
WHERE e.id = p_evento_id
AND e.stato in (100, 200)
AND e.mail_enabled > 0;
-- Seleziona i dati della location associata all'evento.
SELECT l.* INTO v_location
FROM location l
WHERE l.id = v_evento.id_location;
EXCEPTION
-- Se l'evento non viene trovato (o ha le mail disabilitate), la procedura termina.
WHEN NO_DATA_FOUND THEN RETURN;
END;
-- Inizializza il corpo della mail e imposta lo stile HTML.
CMN_MAIL_HTMLUTILS.mailbody := '';
CMN_MAIL_HTMLUTILS.set_style(p_style_id);
-- Costruisce il corpo della mail paragrafo per paragrafo utilizzando il package di utility.
CMN_MAIL_HTMLUTILS.create_paragraph('Gentilissimi,');
CMN_MAIL_HTMLUTILS.create_paragraph(
'in seguito all''invio della nostra proposta per il vostro evento, '||
'desideriamo sapere se avete avuto modo di valutarla e se siete interessati ai nostri servizi.'
);
CMN_MAIL_HTMLUTILS.create_paragraph(
'Saremmo lieti di incontrarvi per una degustazione, così da potervi presentare al meglio le nostre offerte. '||
'Potete prenotare una delle date disponibili direttamente tramite il nostro sito: '||
'<a href="https://www.apollinarecatering.it/degustazioni/">Degustazioni</a>.'
);
CMN_MAIL_HTMLUTILS.create_paragraph(
'Per qualsiasi ulteriore informazione o chiarimento potete scriverci a '||
'<a href="mailto:info@apollinarecatering.it">info@apollinarecatering.it</a>.'
);
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Si prega di non rispondere direttamente a questa email.</b>');
CMN_MAIL_HTMLUTILS.create_paragraph('Cordiali saluti,');
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Ufficio Commerciale</b>');
CMN_MAIL_HTMLUTILS.create_paragraph('📞 +39 0743 45 449');
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Apollinare Catering</b>');
-- Invia la mail usando APEX_MAIL, componendo un oggetto dinamico.
APEX_MAIL.SEND(
p_to => v_evento.cliente_email,
p_from => 'noreply@apollinarecatering.it',
p_bcc => 'monia@apollinarecatering.it, matrimonio@apollinarecatering.it',
--p_bcc => 'monia@apollinarecatering.it, maria@apollinarecatering.it',
p_subj => 'Apollinare richiesta riscontro per evento del '||
TO_CHAR(v_evento.data, 'DD/MM/YYYY')||' presso '||v_location.location,
p_body => CMN_MAIL_HTMLUTILS.mailbody,
p_body_html => CMN_MAIL_HTMLUTILS.mailbody
);
-- Forza l'invio immediato dalla coda di APEX.
APEX_MAIL.PUSH_QUEUE;
END send_richiesta_riscontro_preventivo;
/**
* @descr Invia una mail di sollecito dopo che il cliente ha partecipato a una degustazione.
* @param p_style_id ID dello stile HTML da usare per il corpo della mail.
* @param p_evento_id ID dell'evento a cui la mail fa riferimento.
*/
PROCEDURE send_richiesta_riscontro_post_degustazione (
p_style_id NUMBER DEFAULT 1,
p_evento_id NUMBER
) AS
v_evento eventi%ROWTYPE;
v_location location%ROWTYPE;
BEGIN
-- Imposta la lingua e il formato della data della sessione.
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_LANGUAGE="ITALIAN"';
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_DATE_FORMAT="DD-MON-YYYY"';
-- Recupera i dettagli dell'evento e della location.
BEGIN
-- Seleziona i dati dell'evento solo se le mail sono abilitate.
SELECT e.* INTO v_evento
FROM eventi e
WHERE e.id = p_evento_id
AND e.stato in (200)
AND e.mail_enabled > 0;
-- Seleziona i dati della location.
SELECT l.* INTO v_location
FROM location l
WHERE l.id = v_evento.id_location;
EXCEPTION
-- Se non trova l'evento, esce dalla procedura.
WHEN NO_DATA_FOUND THEN RETURN;
END;
-- Inizializza il corpo della mail e imposta lo stile HTML.
CMN_MAIL_HTMLUTILS.mailbody := '';
CMN_MAIL_HTMLUTILS.set_style(p_style_id);
-- Costruisce il corpo della mail.
CMN_MAIL_HTMLUTILS.create_paragraph('Gentilissimi,');
CMN_MAIL_HTMLUTILS.create_paragraph(
'in seguito alla degustazione effettuata per il vostro evento, '||
'desideriamo sapere se la nostra proposta risponde alle vostre aspettative '||
'e se intendete procedere con la conferma dei servizi.'
);
CMN_MAIL_HTMLUTILS.create_paragraph(
'Per ogni ulteriore chiarimento siamo a vostra completa disposizione: '||
'<a href="mailto:info@apollinarecatering.it">info@apollinarecatering.it</a>.'
);
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Si prega di non rispondere direttamente a questa email.</b>');
CMN_MAIL_HTMLUTILS.create_paragraph('Cordiali saluti,');
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Ufficio Commerciale</b>');
CMN_MAIL_HTMLUTILS.create_paragraph('📞 +39 0743 45 449');
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Apollinare Catering</b>');
-- Invia la mail, componendo un oggetto dinamico con data e location.
APEX_MAIL.SEND(
p_to => v_evento.cliente_email,
p_from => 'noreply@apollinarecatering.it',
p_bcc => 'monia@apollinarecatering.it, matrimonio@apollinarecatering.it',
--p_bcc => 'monia@apollinarecatering.it, maria@apollinarecatering.it',
p_subj => 'Apollinare riscontro post-degustazione evento del '||
TO_CHAR(v_evento.data, 'DD/MM/YYYY')||' presso '||v_location.location,
p_body => CMN_MAIL_HTMLUTILS.mailbody,
p_body_html => CMN_MAIL_HTMLUTILS.mailbody
);
-- Forza l'invio immediato.
APEX_MAIL.PUSH_QUEUE;
END send_richiesta_riscontro_post_degustazione;
/**
* @descr Procedura schedulabile (job) che invia solleciti per i preventivi non confermati.
*/
PROCEDURE send_richiesta_riscontro_preventivo_job AS
BEGIN
-- Itera su tutti gli eventi che soddisfano i criteri per il sollecito.
FOR evt IN (
SELECT e.*
FROM eventi e
WHERE e.stato in (100) -- Stato: Preventivo non confermato
AND e.mail_enabled = 1 -- Mail abilitate
AND TRUNC(e.data_doc) = TRUNC(SYSDATE) - 10 -- Sono passati 10 giorni dalla data del documento.
)
LOOP
-- Per ogni evento trovato, chiama la procedura di invio mail.
send_richiesta_riscontro_preventivo(
p_style_id => 1,
p_evento_id => evt.id
);
END LOOP;
END send_richiesta_riscontro_preventivo_job;
/**
* @descr Procedura schedulabile (job) che invia solleciti dopo una degustazione.
*/
PROCEDURE send_richiesta_riscontro_post_degustazione_job AS
BEGIN
-- Itera su tutti gli eventi che soddisfano i criteri per il sollecito post-degustazione.
FOR evt IN (
SELECT e.*
FROM eventi e
JOIN ( -- Sottoquery per trovare la data della prima degustazione per ogni evento.
SELECT id_evento,
MIN(TRUNC(data)) AS min_data
FROM eventi_det_degust
GROUP BY id_evento ) dm
ON dm.id_evento = e.id
WHERE e.stato = 200 -- Stato: Scheda evento in preparazione
AND e.mail_enabled = 1 -- Mail abilitate
AND dm.min_data = TRUNC(SYSDATE) - 15 -- Sono passati 15 giorni dalla prima degustazione.
)
LOOP
-- Per ogni evento trovato, chiama la procedura di invio mail.
send_richiesta_riscontro_post_degustazione(
p_style_id => 1,
p_evento_id => evt.id
);
END LOOP;
END send_richiesta_riscontro_post_degustazione_job;
-- ===============================================================================
-- IMPLEMENTAZIONE NUOVE PROCEDURE
-- Data creazione: 26/07/2024
-- ===============================================================================
PROCEDURE send_reminder_seconda_caparra (
p_style_id NUMBER DEFAULT 1,
p_evento_id NUMBER
) AS
v_evento eventi%ROWTYPE;
v_location location%ROWTYPE;
BEGIN
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_LANGUAGE="ITALIAN"';
EXECUTE IMMEDIATE 'ALTER SESSION SET NLS_DATE_FORMAT="DD-MON-YYYY"';
-- Recupero i dati dell'evento e della location
BEGIN
SELECT e.* INTO v_evento
FROM eventi e
WHERE e.id = p_evento_id
AND e.mail_enabled > 0;
SELECT l.* INTO v_location
FROM location l
WHERE l.id = v_evento.id_location;
EXCEPTION
WHEN NO_DATA_FOUND THEN
-- Se l'evento non esiste o le mail sono disabilitate, esco.
RETURN;
END;
-- Costruzione del corpo della mail
CMN_MAIL_HTMLUTILS.mailbody := '';
CMN_MAIL_HTMLUTILS.set_style(p_style_id);
CMN_MAIL_HTMLUTILS.create_paragraph('Gentilissimi,');
CMN_MAIL_HTMLUTILS.create_paragraph(
'sperando che tutto proceda per il meglio, desideriamo cortesemente ricordarvi la scadenza relativa alla seconda tranche di pagamento per il vostro evento.'
);
CMN_MAIL_HTMLUTILS.create_paragraph(
'Qualora non fosse ancora stato effettuato, vi invitiamo a procedere con il versamento della seconda caparra, previsto 60 giorni prima della data dellevento, come da accordi e indicato nel preventivo.'
);
CMN_MAIL_HTMLUTILS.create_paragraph(
'Rimaniamo a disposizione per qualsiasi chiarimento e vi ringraziamo sin da ora per la preziosa collaborazione.'
);
CMN_MAIL_HTMLUTILS.create_paragraph('Cordiali saluti,');
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Ufficio Commerciale</b>');
CMN_MAIL_HTMLUTILS.create_paragraph('📞 +39 0743 45 449');
CMN_MAIL_HTMLUTILS.create_paragraph('<b>Apollinare Catering</b>');
-- Invio della mail
APEX_MAIL.SEND(
--p_to => v_evento.cliente_email,
p_to => 'amministrazione@apollinarecatering.it',
p_from => 'noreply@apollinarecatering.it',
p_bcc => 'monia@apollinarecatering.it, matrimonio@apollinarecatering.it',
--p_bcc => 'monia@apollinarecatering.it, maria@apollinarecatering.it',
p_subj => 'Apollinare Promemoria pagamento per evento del '||
TO_CHAR(v_evento.data, 'DD/MM/YYYY'),
p_body => CMN_MAIL_HTMLUTILS.mailbody,
p_body_html => CMN_MAIL_HTMLUTILS.mailbody
);
-- Invio immediato dalla coda
APEX_MAIL.PUSH_QUEUE;
END send_reminder_seconda_caparra;
PROCEDURE send_reminder_seconda_caparra_job AS
BEGIN
-- Scorro tutti gli eventi che necessitano del reminder.
-- La logica ora utilizza la vista GET_EVENTI_DA_PAGARE_ENTRO_65GG
-- per identificare gli eventi che non hanno saldato la caparra.
FOR evt IN (
SELECT
v.id,
v.data -- Seleziono la data per il calcolo del MOD
FROM
GET_EVENTI_DA_PAGARE_ENTRO_65GG v
JOIN
eventi e ON v.id = e.id -- Join per recuperare il flag mail_enabled
WHERE
-- La vista già filtra per stato, importi e finestra di 65 giorni.
-- Aggiungo solo le condizioni specifiche del job.
-- Le mail automatiche devono essere abilitate.
e.mail_enabled = 1
-- La logica MOD assicura l'invio periodico ogni 5 giorni.
AND MOD(65 - (TRUNC(v.data) - TRUNC(SYSDATE)), 5) = 0
)
LOOP
-- Per ogni evento trovato, chiamo la procedura di invio mail.
send_reminder_seconda_caparra(
p_style_id => 1,
p_evento_id => evt.id
);
END LOOP;
END send_reminder_seconda_caparra_job;
END MAIL_PKG;```

341
docs/packages/PLJSON_DYN.md Normal file
View File

@@ -0,0 +1,341 @@
# PLJSON_DYN
## Package Specification
```sql
package pljson_dyn authid current_user as
/*
Copyright (c) 2010 Jonas Krogsboell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
null_as_empty_string boolean not null := true; --varchar2
include_dates boolean not null := true; --date
include_clobs boolean not null := true;
include_blobs boolean not null := false;
include_arrays boolean not null := true; -- pljson_varray or pljson_narray
/* list with objects */
function executeList(stmt varchar2, bindvar pljson default null, cur_num number default null, bindvardateformats pljson default null) return pljson_list;
/* object with lists */
function executeObject(stmt varchar2, bindvar pljson default null, cur_num number default null) return pljson;
/* usage example:
* declare
* res json_list;
* begin
* res := json_dyn.executeList(
* 'select :bindme as one, :lala as two from dual where dummy in :arraybind',
* json('{bindme:"4", lala:123, arraybind:[1, 2, 3, "X"]}')
* );
* res.print;
* end;
*/
/* --11g functions
function executeList(stmt in out sys_refcursor) return json_list;
function executeObject(stmt in out sys_refcursor) return json;
*/
end pljson_dyn;```
## Package Body
```sql
package body pljson_dyn as
/*
-- 11gR2
function executeList(stmt in out sys_refcursor) return json_list as
l_cur number;
begin
l_cur := dbms_sql.to_cursor_number(stmt);
return json_dyn.executeList(null, null, l_cur);
end;
-- 11gR2
function executeObject(stmt in out sys_refcursor) return json as
l_cur number;
begin
l_cur := dbms_sql.to_cursor_number(stmt);
return json_dyn.executeObject(null, null, l_cur);
end;
*/
procedure bind_json(l_cur number, bindvar pljson, bindvardateformats pljson default null) as
keylist pljson_list := bindvar.get_keys();
begin
for i in 1 .. keylist.count loop
if (bindvar.get(i).is_number()) then
dbms_sql.bind_variable(l_cur, ':'||keylist.get(i).get_string(), bindvar.get(i).get_number());
elsif (bindvar.get(i).is_array()) then
declare
v_bind dbms_sql.varchar2_table;
v_arr pljson_list := pljson_list(bindvar.get(i));
begin
for j in 1 .. v_arr.count loop
v_bind(j) := v_arr.get(j).value_of();
end loop;
dbms_sql.bind_array(l_cur, ':'||keylist.get(i).get_string(), v_bind);
end;
else
if bindvardateformats is not null then
if bindvardateformats.exist(keylist.get(i).get_string()) then
dbms_sql.bind_variable(l_cur, ':'||keylist.get(i).get_string(), to_date(bindvar.get(i).value_of(), bindvardateformats.get(keylist.get(i).get_string()).get_string() ));
else
dbms_sql.bind_variable(l_cur, ':'||keylist.get(i).get_string(), bindvar.get(i).value_of());
end if;
else
dbms_sql.bind_variable(l_cur, ':'||keylist.get(i).get_string(), bindvar.get(i).value_of());
end if;
end if;
end loop;
end bind_json;
/* list with objects */
function executeList(stmt varchar2, bindvar pljson, cur_num number, bindvardateformats pljson default null) return pljson_list as
l_cur number;
l_dtbl dbms_sql.desc_tab3;
l_cnt number;
l_status number;
l_val varchar2(4000);
outer_list pljson_list := pljson_list();
inner_obj pljson;
conv number;
read_date date;
read_clob clob;
read_blob blob;
col_type number;
read_varray pljson_varray;
read_narray pljson_narray;
begin
if (cur_num is not null) then
l_cur := cur_num;
else
l_cur := dbms_sql.open_cursor;
dbms_sql.parse(l_cur, stmt, dbms_sql.native);
if (bindvar is not null) then bind_json(l_cur, bindvar, bindvardateformats); end if;
end if;
/* E.I.Sarmas (github.com/dsnz) 2018-05-01 handling of varray, narray in select */
dbms_sql.describe_columns3(l_cur, l_cnt, l_dtbl);
for i in 1..l_cnt loop
col_type := l_dtbl(i).col_type;
--dbms_output.put_line(col_type);
if (col_type = 12) then
dbms_sql.define_column(l_cur, i, read_date);
elsif (col_type = 112) then
dbms_sql.define_column(l_cur, i, read_clob);
elsif (col_type = 113) then
dbms_sql.define_column(l_cur, i, read_blob);
elsif (col_type in (1, 2, 96)) then
dbms_sql.define_column(l_cur, i, l_val, 4000);
/* E.I.Sarmas (github.com/dsnz) 2018-05-01 handling of pljson_varray in select */
elsif (col_type = 109 and l_dtbl(i).col_type_name = 'PLJSON_VARRAY') then
dbms_sql.define_column(l_cur, i, read_varray);
/* E.I.Sarmas (github.com/dsnz) 2018-05-01 handling of pljson_narray in select */
elsif (col_type = 109 and l_dtbl(i).col_type_name = 'PLJSON_NARRAY') then
dbms_sql.define_column(l_cur, i, read_narray);
/* E.I.Sarmas (github.com/dsnz) 2018-05-01 record unhandled col_type */
else
dbms_output.put_line('unhandled col_type =' || col_type);
end if;
end loop;
if (cur_num is null) then l_status := dbms_sql.execute(l_cur); end if;
--loop through rows
while ( dbms_sql.fetch_rows(l_cur) > 0 ) loop
inner_obj := pljson(); --init for each row
inner_obj.check_for_duplicate := 0;
--loop through columns
for i in 1..l_cnt loop
case true
--handling string types
when l_dtbl(i).col_type in (1, 96) then -- varchar2
dbms_sql.column_value(l_cur, i, l_val);
if (l_val is null) then
if (null_as_empty_string) then
inner_obj.put(l_dtbl(i).col_name, ''); --treat as emptystring?
else
inner_obj.put(l_dtbl(i).col_name, pljson_null()); --null
end if;
else
inner_obj.put(l_dtbl(i).col_name, pljson_string(l_val)); --null
end if;
--dbms_output.put_line(l_dtbl(i).col_name||' --> '||l_val||'varchar2' ||l_dtbl(i).col_type);
--handling number types
when l_dtbl(i).col_type = 2 then -- number
dbms_sql.column_value(l_cur, i, l_val);
conv := l_val;
inner_obj.put(l_dtbl(i).col_name, conv);
-- dbms_output.put_line(l_dtbl(i).col_name||' --> '||l_val||'number ' ||l_dtbl(i).col_type);
when l_dtbl(i).col_type = 12 then -- date
if (include_dates) then
dbms_sql.column_value(l_cur, i, read_date);
inner_obj.put(l_dtbl(i).col_name, pljson_ext.to_json_string(read_date));
end if;
--dbms_output.put_line(l_dtbl(i).col_name||' --> '||l_val||'date ' ||l_dtbl(i).col_type);
when l_dtbl(i).col_type = 112 then --clob
if (include_clobs) then
dbms_sql.column_value(l_cur, i, read_clob);
inner_obj.put(l_dtbl(i).col_name, pljson_string(read_clob));
end if;
when l_dtbl(i).col_type = 113 then --blob
if (include_blobs) then
dbms_sql.column_value(l_cur, i, read_blob);
if (dbms_lob.getlength(read_blob) > 0) then
inner_obj.put(l_dtbl(i).col_name, pljson_ext.encode(read_blob));
else
inner_obj.put(l_dtbl(i).col_name, pljson_null());
end if;
end if;
/* E.I.Sarmas (github.com/dsnz) 2018-05-01 handling of pljson_varray in select */
when l_dtbl(i).col_type = 109 and l_dtbl(i).col_type_name = 'PLJSON_VARRAY' then
if (include_arrays) then
dbms_sql.column_value(l_cur, i, read_varray);
inner_obj.put(l_dtbl(i).col_name, pljson_list(read_varray));
end if;
/* E.I.Sarmas (github.com/dsnz) 2018-05-01 handling of pljson_narray in select */
when l_dtbl(i).col_type = 109 and l_dtbl(i).col_type_name = 'PLJSON_NARRAY' then
if (include_arrays) then
dbms_sql.column_value(l_cur, i, read_narray);
inner_obj.put(l_dtbl(i).col_name, pljson_list(read_narray));
end if;
else null; --discard other types
end case;
end loop;
inner_obj.check_for_duplicate := 1;
outer_list.append(inner_obj);
end loop;
dbms_sql.close_cursor(l_cur);
return outer_list;
end executeList;
/* object with lists */
function executeObject(stmt varchar2, bindvar pljson, cur_num number) return pljson as
l_cur number;
l_dtbl dbms_sql.desc_tab;
l_cnt number;
l_status number;
l_val varchar2(4000);
inner_list_names pljson_list := pljson_list();
inner_list_data pljson_list := pljson_list();
data_list pljson_list;
outer_obj pljson := pljson();
conv number;
read_date date;
read_clob clob;
read_blob blob;
col_type number;
begin
if (cur_num is not null) then
l_cur := cur_num;
else
l_cur := dbms_sql.open_cursor;
dbms_sql.parse(l_cur, stmt, dbms_sql.native);
if (bindvar is not null) then bind_json(l_cur, bindvar); end if;
end if;
dbms_sql.describe_columns(l_cur, l_cnt, l_dtbl);
for i in 1..l_cnt loop
col_type := l_dtbl(i).col_type;
if (col_type = 12) then
dbms_sql.define_column(l_cur, i, read_date);
elsif (col_type = 112) then
dbms_sql.define_column(l_cur, i, read_clob);
elsif (col_type = 113) then
dbms_sql.define_column(l_cur, i, read_blob);
elsif (col_type in (1, 2, 96)) then
dbms_sql.define_column(l_cur, i, l_val, 4000);
end if;
end loop;
if (cur_num is null) then l_status := dbms_sql.execute(l_cur); end if;
--build up name_list
for i in 1..l_cnt loop
case l_dtbl(i).col_type
when 1 then inner_list_names.append(l_dtbl(i).col_name);
when 96 then inner_list_names.append(l_dtbl(i).col_name);
when 2 then inner_list_names.append(l_dtbl(i).col_name);
when 12 then if (include_dates) then inner_list_names.append(l_dtbl(i).col_name); end if;
when 112 then if (include_clobs) then inner_list_names.append(l_dtbl(i).col_name); end if;
when 113 then if (include_blobs) then inner_list_names.append(l_dtbl(i).col_name); end if;
else null;
end case;
end loop;
--loop through rows
while ( dbms_sql.fetch_rows(l_cur) > 0 ) loop
data_list := pljson_list();
--loop through columns
for i in 1..l_cnt loop
case true
--handling string types
when l_dtbl(i).col_type in (1, 96) then -- varchar2
dbms_sql.column_value(l_cur, i, l_val);
if (l_val is null) then
if (null_as_empty_string) then
data_list.append(''); --treat as emptystring?
else
data_list.append(pljson_null()); --null
end if;
else
data_list.append(pljson_string(l_val)); --null
end if;
--dbms_output.put_line(l_dtbl(i).col_name||' --> '||l_val||'varchar2' ||l_dtbl(i).col_type);
--handling number types
when l_dtbl(i).col_type = 2 then -- number
dbms_sql.column_value(l_cur, i, l_val);
conv := l_val;
data_list.append(conv);
-- dbms_output.put_line(l_dtbl(i).col_name||' --> '||l_val||'number ' ||l_dtbl(i).col_type);
when l_dtbl(i).col_type = 12 then -- date
if (include_dates) then
dbms_sql.column_value(l_cur, i, read_date);
data_list.append(pljson_ext.to_json_string(read_date));
end if;
--dbms_output.put_line(l_dtbl(i).col_name||' --> '||l_val||'date ' ||l_dtbl(i).col_type);
when l_dtbl(i).col_type = 112 then --clob
if (include_clobs) then
dbms_sql.column_value(l_cur, i, read_clob);
data_list.append(pljson_string(read_clob));
end if;
when l_dtbl(i).col_type = 113 then --blob
if (include_blobs) then
dbms_sql.column_value(l_cur, i, read_blob);
if (dbms_lob.getlength(read_blob) > 0) then
data_list.append(pljson_ext.encode(read_blob));
else
data_list.append(pljson_null());
end if;
end if;
else null; --discard other types
end case;
end loop;
inner_list_data.append(data_list);
end loop;
outer_obj.put('names', inner_list_names);
outer_obj.put('data', inner_list_data);
dbms_sql.close_cursor(l_cur);
return outer_obj;
end executeObject;
end pljson_dyn;```

1105
docs/packages/PLJSON_EXT.md Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,475 @@
# PLJSON_HELPER
## Package Specification
```sql
package pljson_helper as
/* Example:
set serveroutput on;
declare
v_a json;
v_b json;
begin
v_a := json('{a:1, b:{a:null}, e:false}');
v_b := json('{c:3, e:{}, b:{b:2}}');
json_helper.merge(v_a, v_b).print(false);
end;
--
{"a":1,"b":{"a":null,"b":2},"e":{},"c":3}
*/
-- Recursive merge
-- Courtesy of Matt Nolan - edited by Jonas Krogsboell
function merge(p_a_json pljson, p_b_json pljson) return pljson;
-- Join two lists
-- json_helper.join(json_list('[1,2,3]'),json_list('[4,5,6]')) -> [1,2,3,4,5,6]
function join(p_a_list pljson_list, p_b_list pljson_list) return pljson_list;
-- keep only specific keys in json object
-- json_helper.keep(json('{a:1,b:2,c:3,d:4,e:5,f:6}'),json_list('["a","f","c"]')) -> {"a":1,"f":6,"c":3}
function keep(p_json pljson, p_keys pljson_list) return pljson;
-- remove specific keys in json object
-- json_helper.remove(json('{a:1,b:2,c:3,d:4,e:5,f:6}'),json_list('["a","f","c"]')) -> {"b":2,"d":4,"e":5}
function remove(p_json pljson, p_keys pljson_list) return pljson;
--equals
function equals(p_v1 pljson_element, p_v2 pljson_element, exact boolean default true) return boolean;
function equals(p_v1 pljson_element, p_v2 pljson, exact boolean default true) return boolean;
function equals(p_v1 pljson_element, p_v2 pljson_list, exact boolean default true) return boolean;
function equals(p_v1 pljson_element, p_v2 number) return boolean;
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
function equals(p_v1 pljson_element, p_v2 binary_double) return boolean;
function equals(p_v1 pljson_element, p_v2 varchar2) return boolean;
function equals(p_v1 pljson_element, p_v2 boolean) return boolean;
function equals(p_v1 pljson_element, p_v2 clob) return boolean;
function equals(p_v1 pljson, p_v2 pljson, exact boolean default true) return boolean;
function equals(p_v1 pljson_list, p_v2 pljson_list, exact boolean default true) return boolean;
--contains json, json_value
--contains json_list, json_value
function contains(p_v1 pljson, p_v2 pljson_element, exact boolean default false) return boolean;
function contains(p_v1 pljson, p_v2 pljson, exact boolean default false) return boolean;
function contains(p_v1 pljson, p_v2 pljson_list, exact boolean default false) return boolean;
function contains(p_v1 pljson, p_v2 number, exact boolean default false) return boolean;
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
function contains(p_v1 pljson, p_v2 binary_double, exact boolean default false) return boolean;
function contains(p_v1 pljson, p_v2 varchar2, exact boolean default false) return boolean;
function contains(p_v1 pljson, p_v2 boolean, exact boolean default false) return boolean;
function contains(p_v1 pljson, p_v2 clob, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 pljson_element, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 pljson, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 pljson_list, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 number, exact boolean default false) return boolean;
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
function contains(p_v1 pljson_list, p_v2 binary_double, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 varchar2, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 boolean, exact boolean default false) return boolean;
function contains(p_v1 pljson_list, p_v2 clob, exact boolean default false) return boolean;
end pljson_helper;```
## Package Body
```sql
package body pljson_helper as
--recursive merge
function merge(p_a_json pljson, p_b_json pljson) return pljson as
l_json pljson;
l_jv pljson_element;
l_indx number;
l_recursive pljson_element;
begin
--
-- Initialize our return object
--
l_json := p_a_json;
-- loop through p_b_json
l_indx := p_b_json.json_data.first;
loop
exit when l_indx is null;
l_jv := p_b_json.json_data(l_indx);
if (l_jv.is_object) then
--recursive
l_recursive := l_json.get(l_jv.mapname);
if (l_recursive is not null and l_recursive.is_object) then
l_json.put(l_jv.mapname, merge(pljson(l_recursive), pljson(l_jv)));
else
l_json.put(l_jv.mapname, l_jv);
end if;
else
l_json.put(l_jv.mapname, l_jv);
end if;
--increment
l_indx := p_b_json.json_data.next(l_indx);
end loop;
return l_json;
end merge;
-- join two lists
function join(p_a_list pljson_list, p_b_list pljson_list) return pljson_list as
l_json_list pljson_list := p_a_list;
begin
for indx in 1 .. p_b_list.count loop
l_json_list.append(p_b_list.get(indx));
end loop;
return l_json_list;
end join;
-- keep keys.
function keep(p_json pljson, p_keys pljson_list) return pljson as
l_json pljson := pljson();
mapname varchar2(4000);
begin
for i in 1 .. p_keys.count loop
mapname := p_keys.get(i).get_string();
if (p_json.exist(mapname)) then
l_json.put(mapname, p_json.get(mapname));
end if;
end loop;
return l_json;
end keep;
-- drop keys.
function remove(p_json pljson, p_keys pljson_list) return pljson as
l_json pljson := p_json;
begin
for i in 1 .. p_keys.count loop
l_json.remove(p_keys.get(i).get_string());
end loop;
return l_json;
end remove;
--equals functions
function equals(p_v1 pljson_element, p_v2 number) return boolean as
begin
if (p_v2 is null) then
return p_v1.is_null;
end if;
if (not p_v1.is_number) then
return false;
end if;
return p_v2 = p_v1.get_number();
end;
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
function equals(p_v1 pljson_element, p_v2 binary_double) return boolean as
begin
if (p_v2 is null) then
return p_v1.is_null;
end if;
if (not p_v1.is_number) then
return false;
end if;
return p_v2 = p_v1.get_double();
end;
function equals(p_v1 pljson_element, p_v2 boolean) return boolean as
begin
if (p_v2 is null) then
return p_v1.is_null;
end if;
if (not p_v1.is_bool) then
return false;
end if;
return p_v2 = p_v1.get_bool();
end;
function equals(p_v1 pljson_element, p_v2 varchar2) return boolean as
begin
if (p_v2 is null) then
return (p_v1.is_null or p_v1.get_string() is null);
end if;
if (not p_v1.is_string) then
return false;
end if;
return p_v2 = p_v1.get_string();
end;
function equals(p_v1 pljson_element, p_v2 clob) return boolean as
my_clob clob;
res boolean;
begin
if (p_v2 is null) then
return p_v1.is_null;
end if;
if (not p_v1.is_string) then
return false;
end if;
/*
my_clob := empty_clob();
dbms_lob.createtemporary(my_clob, true);
p_v1.get_string(my_clob);
*/
my_clob := p_v1.get_clob();
res := dbms_lob.compare(p_v2, my_clob) = 0;
/*dbms_lob.freetemporary(my_clob);*/
return res;
end;
function equals(p_v1 pljson_element, p_v2 pljson_element, exact boolean) return boolean as
begin
if (p_v2 is null or p_v2.is_null) then
return (p_v1 is null or p_v1.is_null);
end if;
if (p_v2.is_number) then return equals(p_v1, p_v2.get_number); end if;
if (p_v2.is_bool) then return equals(p_v1, p_v2.get_bool); end if;
if (p_v2.is_object) then return equals(p_v1, pljson(p_v2), exact); end if;
if (p_v2.is_array) then return equals(p_v1, pljson_list(p_v2), exact); end if;
if (p_v2.is_string) then
if (treat(p_v2 as pljson_string).extended_str is null) then
return equals(p_v1, p_v2.get_string);
else
declare
my_clob clob; res boolean;
begin
/*
my_clob := empty_clob();
dbms_lob.createtemporary(my_clob, true);
p_v2.get_string(my_clob);
*/
my_clob := p_v2.get_clob();
res := equals(p_v1, my_clob);
/*dbms_lob.freetemporary(my_clob);*/
return res;
end;
end if;
end if;
return false; --should never happen
end;
function equals(p_v1 pljson_element, p_v2 pljson_list, exact boolean) return boolean as
cmp pljson_list;
res boolean := true;
begin
-- p_v1.print(false);
-- p_v2.print(false);
-- dbms_output.put_line('labc1'||case when exact then 'X' else 'U' end);
if (p_v2 is null) then
return p_v1.is_null;
end if;
if (not p_v1.is_array) then
return false;
end if;
-- dbms_output.put_line('labc2'||case when exact then 'X' else 'U' end);
cmp := pljson_list(p_v1);
if (cmp.count != p_v2.count and exact) then return false; end if;
-- dbms_output.put_line('labc3'||case when exact then 'X' else 'U' end);
if (exact) then
for i in 1 .. cmp.count loop
res := equals(cmp.get(i), p_v2.get(i), exact);
if (not res) then return res; end if;
end loop;
else
-- dbms_output.put_line('labc4'||case when exact then 'X' else 'U' end);
if (p_v2.count > cmp.count) then return false; end if;
-- dbms_output.put_line('labc5'||case when exact then 'X' else 'U' end);
--match sublist here!
for x in 0 .. (cmp.count-p_v2.count) loop
-- dbms_output.put_line('labc7'||x);
for i in 1 .. p_v2.count loop
res := equals(cmp.get(x+i), p_v2.get(i), exact);
if (not res) then
goto next_index;
end if;
end loop;
return true;
<<next_index>>
null;
end loop;
-- dbms_output.put_line('labc7'||case when exact then 'X' else 'U' end);
return false; --no match
end if;
return res;
end;
function equals(p_v1 pljson_element, p_v2 pljson, exact boolean) return boolean as
cmp pljson;
res boolean := true;
begin
-- p_v1.print(false);
-- p_v2.print(false);
-- dbms_output.put_line('abc1');
if (p_v2 is null) then
return p_v1.is_null;
end if;
if (not p_v1.is_object) then
return false;
end if;
cmp := pljson(p_v1);
-- dbms_output.put_line('abc2');
if (cmp.count != p_v2.count and exact) then return false; end if;
-- dbms_output.put_line('abc3');
declare
k1 pljson_list := p_v2.get_keys();
key_index number;
begin
for i in 1 .. k1.count loop
key_index := cmp.index_of(k1.get(i).get_string());
if (key_index = -1) then return false; end if;
if (exact) then
if (not equals(p_v2.get(i), cmp.get(key_index), true)) then return false; end if;
else
--non exact
declare
v1 pljson_element := cmp.get(key_index);
v2 pljson_element := p_v2.get(i);
begin
-- dbms_output.put_line('abc3 1/2');
-- v1.print(false);
-- v2.print(false);
if (v1.is_object and v2.is_object) then
if (not equals(v1, v2, false)) then return false; end if;
elsif (v1.is_array and v2.is_array) then
if (not equals(v1, v2, false)) then return false; end if;
else
if (not equals(v1, v2, true)) then return false; end if;
end if;
end;
end if;
end loop;
end;
-- dbms_output.put_line('abc4');
return true;
end;
function equals(p_v1 pljson, p_v2 pljson, exact boolean) return boolean as
begin
return equals(p_v1, p_v2, exact);
end;
function equals(p_v1 pljson_list, p_v2 pljson_list, exact boolean) return boolean as
begin
return equals(p_v1, p_v2, exact);
end;
--contain
function contains(p_v1 pljson, p_v2 pljson_element, exact boolean) return boolean as
v_values pljson_list;
begin
if (equals(p_v1, p_v2, exact)) then return true; end if;
v_values := p_v1.get_values();
for i in 1 .. v_values.count loop
declare
v_val pljson_element := v_values.get(i);
begin
if (v_val.is_object) then
if (contains(pljson(v_val), p_v2, exact)) then return true; end if;
end if;
if (v_val.is_array) then
if (contains(pljson_list(v_val), p_v2, exact)) then return true; end if;
end if;
if (equals(v_val, p_v2, exact)) then return true; end if;
end;
end loop;
return false;
end;
function contains(p_v1 pljson_list, p_v2 pljson_element, exact boolean) return boolean as
begin
if (equals(p_v1, p_v2, exact)) then return true; end if;
for i in 1 .. p_v1.count loop
declare
v_val pljson_element := p_v1.get(i);
begin
if (v_val.is_object) then
if (contains(pljson(v_val), p_v2, exact)) then return true; end if;
end if;
if (v_val.is_array) then
if (contains(pljson_list(v_val), p_v2, exact)) then return true; end if;
end if;
if (equals(v_val, p_v2, exact)) then return true; end if;
end;
end loop;
return false;
end;
function contains(p_v1 pljson, p_v2 pljson, exact boolean ) return boolean as
begin return contains(p_v1, p_v2, exact); end;
function contains(p_v1 pljson, p_v2 pljson_list, exact boolean ) return boolean as
begin return contains(p_v1, p_v2, exact); end;
function contains(p_v1 pljson, p_v2 number, exact boolean ) return boolean as begin
return contains(p_v1, pljson_number(p_v2), exact); end;
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
function contains(p_v1 pljson, p_v2 binary_double, exact boolean ) return boolean as begin
return contains(p_v1, pljson_number(p_v2), exact); end;
function contains(p_v1 pljson, p_v2 varchar2, exact boolean ) return boolean as begin
return contains(p_v1, pljson_string(p_v2), exact); end;
function contains(p_v1 pljson, p_v2 boolean, exact boolean ) return boolean as begin
return contains(p_v1, pljson_bool(p_v2), exact); end;
function contains(p_v1 pljson, p_v2 clob, exact boolean ) return boolean as begin
return contains(p_v1, pljson_string(p_v2), exact); end;
function contains(p_v1 pljson_list, p_v2 pljson, exact boolean ) return boolean as begin
return contains(p_v1, p_v2, exact); end;
function contains(p_v1 pljson_list, p_v2 pljson_list, exact boolean ) return boolean as begin
return contains(p_v1, p_v2, exact); end;
function contains(p_v1 pljson_list, p_v2 number, exact boolean ) return boolean as begin
return contains(p_v1, pljson_number(p_v2), exact); end;
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
function contains(p_v1 pljson_list, p_v2 binary_double, exact boolean ) return boolean as begin
return contains(p_v1, pljson_number(p_v2), exact); end;
function contains(p_v1 pljson_list, p_v2 varchar2, exact boolean ) return boolean as begin
return contains(p_v1, pljson_string(p_v2), exact); end;
function contains(p_v1 pljson_list, p_v2 boolean, exact boolean ) return boolean as begin
return contains(p_v1, pljson_bool(p_v2), exact); end;
function contains(p_v1 pljson_list, p_v2 clob, exact boolean ) return boolean as begin
return contains(p_v1, pljson_string(p_v2), exact); end;
end pljson_helper;```

310
docs/packages/PLJSON_ML.md Normal file
View File

@@ -0,0 +1,310 @@
# PLJSON_ML
## Package Specification
```sql
package pljson_ml as
/*
Copyright (c) 2010 Jonas Krogsboell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/* This package contains extra methods to lookup types and
an easy way of adding date values in json - without changing the structure */
jsonml_stylesheet xmltype := null;
function xml2json(xml in xmltype) return pljson_list;
function xmlstr2json(xmlstr in varchar2) return pljson_list;
end pljson_ml;```
## Package Body
```sql
package body pljson_ml as
function get_jsonml_stylesheet return xmltype;
function xml2json(xml in xmltype) return pljson_list as
l_json xmltype;
l_returnvalue clob;
begin
l_json := xml.transform (get_jsonml_stylesheet);
l_returnvalue := l_json.getclobval();
l_returnvalue := dbms_xmlgen.convert (l_returnvalue, dbms_xmlgen.entity_decode);
--dbms_output.put_line(l_returnvalue);
return pljson_list(l_returnvalue);
end xml2json;
function xmlstr2json(xmlstr in varchar2) return pljson_list as
begin
return xml2json(xmltype(xmlstr));
end xmlstr2json;
function get_jsonml_stylesheet return xmltype as
begin
if (jsonml_stylesheet is null) then
jsonml_stylesheet := xmltype('<?xml version="1.0" encoding="UTF-8"?>
<!--
JsonML.xslt
Created: 2006-11-15-0551
Modified: 2009-02-14-0927
Released under an open-source license:
http://jsonml.org/License.htm
This transformation converts any XML document into JsonML.
It omits processing-instructions and comment-nodes.
To enable comment-nodes to be emitted as JavaScript comments,
uncomment the Comment() template.
-->
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"
media-type="application/json"
encoding="UTF-8"
indent="no"
omit-xml-declaration="yes" />
<!-- constants -->
<xsl:variable name="XHTML"
select="''http://www.w3.org/1999/xhtml''" />
<xsl:variable name="START_ELEM"
select="''[''" />
<xsl:variable name="END_ELEM"
select="'']''" />
<xsl:variable name="VALUE_DELIM"
select="'',''" />
<xsl:variable name="START_ATTRIB"
select="''{''" />
<xsl:variable name="END_ATTRIB"
select="''}''" />
<xsl:variable name="NAME_DELIM"
select="'':''" />
<xsl:variable name="STRING_DELIM"
select="''&#x22;''" />
<xsl:variable name="START_COMMENT"
select="''/*''" />
<xsl:variable name="END_COMMENT"
select="''*/''" />
<!-- root-node -->
<xsl:template match="/">
<xsl:apply-templates select="*" />
</xsl:template>
<!-- comments -->
<xsl:template match="comment()">
<!-- uncomment to support JSON comments -->
<!--
<xsl:value-of select="$START_COMMENT" />
<xsl:value-of select="."
disable-output-escaping="yes" />
<xsl:value-of select="$END_COMMENT" />
-->
</xsl:template>
<!-- elements -->
<xsl:template match="*">
<xsl:value-of select="$START_ELEM" />
<!-- tag-name string -->
<xsl:value-of select="$STRING_DELIM" />
<xsl:choose>
<xsl:when test="namespace-uri()=$XHTML">
<xsl:value-of select="local-name()" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$STRING_DELIM" />
<!-- attribute object -->
<xsl:if test="count(@*)>0">
<xsl:value-of select="$VALUE_DELIM" />
<xsl:value-of select="$START_ATTRIB" />
<xsl:for-each select="@*">
<xsl:if test="position()>1">
<xsl:value-of select="$VALUE_DELIM" />
</xsl:if>
<xsl:apply-templates select="." />
</xsl:for-each>
<xsl:value-of select="$END_ATTRIB" />
</xsl:if>
<!-- child elements and text-nodes -->
<xsl:for-each select="*|text()">
<xsl:value-of select="$VALUE_DELIM" />
<xsl:apply-templates select="." />
</xsl:for-each>
<xsl:value-of select="$END_ELEM" />
</xsl:template>
<!-- text-nodes -->
<xsl:template match="text()">
<xsl:call-template name="escape-string">
<xsl:with-param name="value"
select="." />
</xsl:call-template>
</xsl:template>
<!-- attributes -->
<xsl:template match="@*">
<xsl:value-of select="$STRING_DELIM" />
<xsl:choose>
<xsl:when test="namespace-uri()=$XHTML">
<xsl:value-of select="local-name()" />
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="name()" />
</xsl:otherwise>
</xsl:choose>
<xsl:value-of select="$STRING_DELIM" />
<xsl:value-of select="$NAME_DELIM" />
<xsl:call-template name="escape-string">
<xsl:with-param name="value"
select="." />
</xsl:call-template>
</xsl:template>
<!-- escape-string: quotes and escapes -->
<xsl:template name="escape-string">
<xsl:param name="value" />
<xsl:value-of select="$STRING_DELIM" />
<xsl:if test="string-length($value)>0">
<xsl:variable name="escaped-whacks">
<!-- escape backslashes -->
<xsl:call-template name="string-replace">
<xsl:with-param name="value"
select="$value" />
<xsl:with-param name="find"
select="''\''" />
<xsl:with-param name="replace"
select="''\\''" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="escaped-LF">
<!-- escape line feeds -->
<xsl:call-template name="string-replace">
<xsl:with-param name="value"
select="$escaped-whacks" />
<xsl:with-param name="find"
select="''&#x0A;''" />
<xsl:with-param name="replace"
select="''\n''" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="escaped-CR">
<!-- escape carriage returns -->
<xsl:call-template name="string-replace">
<xsl:with-param name="value"
select="$escaped-LF" />
<xsl:with-param name="find"
select="''&#x0D;''" />
<xsl:with-param name="replace"
select="''\r''" />
</xsl:call-template>
</xsl:variable>
<xsl:variable name="escaped-tabs">
<!-- escape tabs -->
<xsl:call-template name="string-replace">
<xsl:with-param name="value"
select="$escaped-CR" />
<xsl:with-param name="find"
select="''&#x09;''" />
<xsl:with-param name="replace"
select="''\t''" />
</xsl:call-template>
</xsl:variable>
<!-- escape quotes -->
<xsl:call-template name="string-replace">
<xsl:with-param name="value"
select="$escaped-tabs" />
<xsl:with-param name="find"
select="''&quot;''" />
<xsl:with-param name="replace"
select="''\&quot;''" />
</xsl:call-template>
</xsl:if>
<xsl:value-of select="$STRING_DELIM" />
</xsl:template>
<!-- string-replace: replaces occurances of one string with another -->
<xsl:template name="string-replace">
<xsl:param name="value" />
<xsl:param name="find" />
<xsl:param name="replace" />
<xsl:choose>
<xsl:when test="contains($value,$find)">
<!-- replace and call recursively on next -->
<xsl:value-of select="substring-before($value,$find)"
disable-output-escaping="yes" />
<xsl:value-of select="$replace"
disable-output-escaping="yes" />
<xsl:call-template name="string-replace">
<xsl:with-param name="value"
select="substring-after($value,$find)" />
<xsl:with-param name="find"
select="$find" />
<xsl:with-param name="replace"
select="$replace" />
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<!-- no replacement necessary -->
<xsl:value-of select="$value"
disable-output-escaping="yes" />
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>');
end if;
return jsonml_stylesheet;
end get_jsonml_stylesheet;
end pljson_ml;```

View File

@@ -0,0 +1,171 @@
# PLJSON_OBJECT_CACHE
## Package Specification
```sql
package pljson_object_cache as
/* E.I.Sarmas (github.com/dsnz) 2020-04-18 object cache to speed up internal operations */
/* !!! NOTE: this package is used internally by pljson and it's not part of the api !!! */
/* index by string of "id.path" or "path" */
type pljson_element_tab is table of pljson_element index by varchar2(250);
last_id number := 0;
pljson_element_cache pljson_element_tab;
cache_reqs number := 0;
cache_hits number := 0;
cache_invalid_reqs number := 0;
type vset is table of varchar2(1) index by varchar2(250);
names_set vset;
procedure set_names_set(names pljson_varray);
function in_names_set(a_name varchar2) return boolean;
procedure reset;
procedure flush;
procedure print_stats;
function next_id return number;
function object_key(elem pljson_element, piece varchar2) return varchar2;
function get(key varchar2) return pljson_element;
procedure set(key varchar2, val pljson_element);
end;```
## Package Body
```sql
package body pljson_object_cache as
/* E.I.Sarmas (github.com/dsnz) 2020-04-18 object cache to speed up internal operations */
/* !!! NOTE: this package is used internally by pljson and it's not part of the api !!! */
procedure set_names_set(names pljson_varray) is
begin
if names.COUNT = 0 then
return;
end if;
for i in names.FIRST .. names.LAST loop
names_set(names(i)) := '1';
end loop;
end;
function in_names_set(a_name varchar2) return boolean is
begin
if names_set.exists(a_name) then
return true;
else
return false;
end if;
end;
procedure reset is
begin
last_id := 0;
flush;
end;
procedure flush is
begin
pljson_element_cache.delete;
cache_reqs := 0;
cache_hits := 0;
cache_invalid_reqs := 0;
end;
procedure print_stats is
begin
dbms_output.put_line('reqs = ' || cache_reqs);
dbms_output.put_line('hits = ' || cache_hits);
dbms_output.put_line('invalid reqs = ' || cache_invalid_reqs);
dbms_output.put_line('cache count = ' || pljson_element_cache.count);
dbms_output.put_line('id count = ' || last_id);
end;
function next_id return number is
begin
last_id := last_id + 1;
return last_id;
end;
function object_key(elem pljson_element, piece varchar2) return varchar2 is
key varchar2(250);
begin
if elem.object_id is null or elem.object_id = 0 then
cache_invalid_reqs := cache_invalid_reqs + 1;
return null;
end if;
key := to_char(elem.object_id)||'.'||piece;
return key;
end;
function get(key varchar2) return pljson_element is
cache_key varchar2(32767);
begin
cache_key := key;
if cache_key is null then
cache_key := '$';
end if;
cache_reqs := cache_reqs + 1;
if pljson_element_cache.exists(cache_key) then
cache_hits := cache_hits + 1;
return pljson_element_cache(cache_key);
else
return null;
end if;
end;
procedure set(key varchar2, val pljson_element) is
cache_key varchar2(32767);
begin
cache_key := key;
if cache_key is null then
cache_key := '$';
end if;
--dbms_output.put_line('caching: ' || cache_key);
pljson_element_cache(cache_key) := val;
end;
/*
-- experimental, ignore
-- to use with 'get_json_element'
function get_piece(elem pljson, piece varchar2) return pljson_element is
key varchar2(250);
val pljson_element;
begin
key := object_cache.object_key(elem, piece);
if key is null then
return elem.get(piece);
end if;
val := object_cache.get(key);
if val is not null then
return val;
else
val := elem.get(piece);
object_cache.set(key, val);
return val;
end if;
end;
function get_piece(elem pljson_list, piece varchar2) return pljson_element is
key varchar2(250);
val pljson_element;
begin
key := object_cache.object_key(elem, piece);
if key is null then
return elem.get(piece);
end if;
val := object_cache.get(key);
if val is not null then
return val;
else
val := elem.get(piece);
object_cache.set(key, val);
return val;
end if;
end;
*/
end;```

View File

@@ -0,0 +1,986 @@
# PLJSON_PARSER
## Package Specification
```sql
package pljson_parser as
/*
Copyright (c) 2010 Jonas Krogsboell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/** Internal type for processing. */
/* scanner tokens:
'{', '}', ',', ':', '[', ']', STRING, NUMBER, TRUE, FALSE, NULL
*/
type rToken IS RECORD (
type_name VARCHAR2(7),
line PLS_INTEGER,
col PLS_INTEGER,
data VARCHAR2(32767),
data_overflow clob); -- max_string_size
type lTokens is table of rToken index by pls_integer;
type json_src is record (len number, offset number, offset_chars number, src varchar2(32767), s_clob clob);
json_strict boolean not null := false;
ucs2_exception EXCEPTION;
pragma exception_init(ucs2_exception, -22831);
function lengthcc(buf clob) return number;
function next_char(indx number, s in out nocopy json_src) return varchar2;
function next_char2(indx number, s in out nocopy json_src, amount number default 1) return varchar2;
function parseObj(tokens lTokens, indx in out nocopy pls_integer) return pljson;
function prepareClob(buf in clob) return pljson_parser.json_src;
function prepareVarchar2(buf in varchar2) return pljson_parser.json_src;
function lexer(jsrc in out nocopy json_src) return lTokens;
procedure print_token(t rToken);
/**
* <p>Primary parsing method. It can parse a JSON object.</p>
*
* @return An instance of <code>pljson</code>.
* @throws PARSER_ERROR -20101 when invalid input found.
* @throws SCANNER_ERROR -20100 when lexing fails.
*/
function parser(str varchar2) return pljson;
function parse_list(str varchar2) return pljson_list;
function parse_any(str varchar2) return pljson_element;
function parser(str clob) return pljson;
function parse_list(str clob) return pljson_list;
function parse_any(str clob) return pljson_element;
procedure remove_duplicates(obj in out nocopy pljson);
function get_version return varchar2;
end pljson_parser;```
## Package Body
```sql
package body pljson_parser as
/*
Copyright (c) 2009 Jonas Krogsboell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
decimalpoint varchar2(1 char) := '.';
/* moved to package spec
ucs2_exception EXCEPTION;
pragma exception_init(ucs2_exception, -22831);
*/
function lengthcc(buf clob) return number as
offset number := 0;
len number := 0;
src varchar2(32767);
src_len number;
begin
while true loop
begin
src := dbms_lob.substr(buf, 4000, offset+1);
exception
when ucs2_exception then
src := dbms_lob.substr(buf, 3999, offset+1);
end;
exit when src is null;
len := len + length(src);
offset := offset + length2(src);
--dbms_output.put_line('offset = ' || offset || ' len = ' || len);
end loop;
return len;
end;
procedure update_decimalpoint as
begin
select substr(value, 1, 1)
into decimalpoint
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS';
end update_decimalpoint;
/* type json_src is record (len number, offset number, src varchar2(32767), s_clob clob); */
/* assertions
offset: contains 0-base offset of buffer,
so 1-st entry is offset + 1, 4000-th entry = offset + 4000
src: contains offset + 1 .. offset + 4000, ex. 1..4000, 4001..8000, etc.
*/
function next_char(indx number, s in out nocopy json_src) return varchar2 as
begin
if (indx > s.len) then return null; end if;
--right offset?
/* if (indx > 4000 + s.offset or indx < s.offset) then */
/* fix for issue #37 */
/* code before fix for issue #169
if (indx > 4000 + s.offset or indx <= s.offset) then
s.offset := indx - (indx mod 4000);
-- addon fix for issue #37
if s.offset = indx then
s.offset := s.offset - 4000;
end if;
s.src := dbms_lob.substr(s.s_clob, 4000, s.offset+1);
end if;
--read from s.src
return substr(s.src, indx-s.offset, 1);
*/
/* use of length, so works correctly for 4-byte unicode characters (issue #169) */
/* lengthc does not work (issue #190) */
if (indx > length(s.src) + s.offset_chars) then
while (indx > length(s.src) + s.offset_chars) loop
s.offset_chars := s.offset_chars + length(s.src);
s.offset := s.offset + length2(s.src);
/* exception check, so works correctly for 4-byte unicode characters (issue #169) */
begin
s.src := dbms_lob.substr(s.s_clob, 4000, s.offset+1);
exception
when ucs2_exception then
s.src := dbms_lob.substr(s.s_clob, 3999, s.offset+1);
end;
end loop;
elsif (indx <= s.offset_chars) then
s.offset_chars := 0;
s.offset := 0;
/* exception check, so works correctly for 4-byte unicode characters (issue #169) */
begin
s.src := dbms_lob.substr(s.s_clob, 4000, s.offset+1);
exception
when ucs2_exception then
s.src := dbms_lob.substr(s.s_clob, 3999, s.offset+1);
end;
while (indx > length(s.src) + s.offset_chars) loop
s.offset_chars := s.offset_chars + length(s.src);
s.offset := s.offset + length2(s.src);
/* exception check, so works correctly for 4-byte unicode characters (issue #169) */
begin
s.src := dbms_lob.substr(s.s_clob, 4000, s.offset+1);
exception
when ucs2_exception then
s.src := dbms_lob.substr(s.s_clob, 3999, s.offset+1);
end;
end loop;
end if;
--dbms_output.put_line('indx: ' || indx || ' offset: ' || s.offset || ' (chars: ' || s.offset_chars || ') src chars: ' || length(s.src));
--read from s.src
return substr(s.src, indx-s.offset_chars, 1);
end;
function next_char2(indx number, s in out nocopy json_src, amount number default 1) return varchar2 as
buf varchar2(32767) := '';
begin
for i in 1..amount loop
buf := buf || next_char(indx-1+i, s);
end loop;
return buf;
end;
function prepareClob(buf clob) return pljson_parser.json_src as
temp pljson_parser.json_src;
begin
temp.s_clob := buf;
temp.offset_chars := 0;
temp.offset := 0;
/* exception check, so works correctly for 4-byte unicode characters (issue #169) */
begin
temp.src := dbms_lob.substr(buf, 4000, temp.offset+1);
exception
when ucs2_exception then
temp.src := dbms_lob.substr(buf, 3999, temp.offset+1);
end;
/* use of lengthcc, so works correctly for 4-byte unicode characters (issue #169) */
temp.len := lengthcc(buf); --dbms_lob.getlength(buf);
return temp;
end;
function prepareVarchar2(buf varchar2) return pljson_parser.json_src as
temp pljson_parser.json_src;
begin
temp.s_clob := buf;
temp.offset_chars := 0;
temp.offset := 0;
temp.src := substr(buf, 1, 4000);
temp.len := length(buf);
return temp;
end;
procedure debug(text varchar2) as
begin
dbms_output.put_line(text);
end;
procedure print_token(t rToken) as
begin
dbms_output.put_line('Line: '||t.line||' - Column: '||t.col||' - Type: '||t.type_name||' - Content: '||t.data);
end print_token;
/* SCANNER FUNCTIONS START */
procedure s_error(text varchar2, line number, col number) as
begin
raise_application_error(-20100, 'JSON Scanner exception @ line: '||line||' column: '||col||' - '||text);
end;
procedure s_error(text varchar2, tok rToken) as
begin
raise_application_error(-20100, 'JSON Scanner exception @ line: '||tok.line||' column: '||tok.col||' - '||text);
end;
function mt(t varchar2, l pls_integer, c pls_integer, d varchar2) return rToken as
token rToken;
begin
token.type_name := t;
token.line := l;
token.col := c;
token.data := d;
return token;
end;
function lexNumber(jsrc in out nocopy json_src, tok in out nocopy rToken, indx in out nocopy pls_integer) return pls_integer as
numbuf varchar2(4000) := '';
buf varchar2(4);
checkLoop boolean;
begin
buf := next_char(indx, jsrc);
if (buf = '-') then numbuf := '-'; indx := indx + 1; end if;
buf := next_char(indx, jsrc);
--0 or [1-9]([0-9])*
if (buf = '0') then
numbuf := numbuf || '0'; indx := indx + 1;
buf := next_char(indx, jsrc);
elsif (buf >= '1' and buf <= '9') then
numbuf := numbuf || buf; indx := indx + 1;
--read digits
buf := next_char(indx, jsrc);
while (buf >= '0' and buf <= '9') loop
numbuf := numbuf || buf; indx := indx + 1;
buf := next_char(indx, jsrc);
end loop;
end if;
--fraction
if (buf = '.') then
numbuf := numbuf || buf; indx := indx + 1;
buf := next_char(indx, jsrc);
checkLoop := FALSE;
while (buf >= '0' and buf <= '9') loop
checkLoop := TRUE;
numbuf := numbuf || buf; indx := indx + 1;
buf := next_char(indx, jsrc);
end loop;
if (not checkLoop) then
s_error('Expected: digits in fraction', tok);
end if;
end if;
--exp part
if (buf in ('e', 'E')) then
numbuf := numbuf || buf; indx := indx + 1;
buf := next_char(indx, jsrc);
if (buf = '+' or buf = '-') then
numbuf := numbuf || buf; indx := indx + 1;
buf := next_char(indx, jsrc);
end if;
checkLoop := FALSE;
while (buf >= '0' and buf <= '9') loop
checkLoop := TRUE;
numbuf := numbuf || buf; indx := indx + 1;
buf := next_char(indx, jsrc);
end loop;
if (not checkLoop) then
s_error('Expected: digits in exp', tok);
end if;
end if;
tok.data := numbuf;
return indx;
end lexNumber;
-- [a-zA-Z]([a-zA-Z0-9])*
function lexName(jsrc in out nocopy json_src, tok in out nocopy rToken, indx in out nocopy pls_integer) return pls_integer as
varbuf varchar2(32767) := '';
buf varchar(4);
num number;
begin
buf := next_char(indx, jsrc);
while (REGEXP_LIKE(buf, '^[[:alnum:]\_]$', 'i')) loop
varbuf := varbuf || buf;
indx := indx + 1;
buf := next_char(indx, jsrc);
if (buf is null) then
goto retname;
--debug('Premature string ending');
end if;
end loop;
<<retname>>
--could check for reserved keywords here
--debug(varbuf);
tok.data := varbuf;
return indx-1;
end lexName;
procedure updateClob(v_extended in out nocopy clob, v_str varchar2) as
begin
/* use of length2, so works correctly for 4-byte unicode characters (issue #169) */
dbms_lob.writeappend(v_extended, length2(v_str), v_str);
end updateClob;
function lexString(jsrc in out nocopy json_src, tok in out nocopy rToken, indx in out nocopy pls_integer, endChar char) return pls_integer as
v_extended clob := null; v_count number := 0;
varbuf varchar2(32767) := '';
buf varchar(4);
wrong boolean;
max_string_chars number := 5000; /* chunk size, less than this number may be copied */
begin
indx := indx + 1;
buf := next_char(indx, jsrc);
while (buf != endChar) loop
--clob control
if (v_count > 8191) then --crazy oracle error (16383 is the highest working length with unistr - 8192 choosen to be safe)
if (v_extended is null) then
v_extended := empty_clob();
dbms_lob.createtemporary(v_extended, true);
end if;
updateClob(v_extended, unistr(varbuf));
varbuf := ''; v_count := 0;
end if;
if (buf = Chr(13) or buf = CHR(9) or buf = CHR(10)) then
s_error('Control characters not allowed (CHR(9),CHR(10),CHR(13))', tok);
end if;
if (buf = '\') then
--varbuf := varbuf || buf;
indx := indx + 1;
buf := next_char(indx, jsrc);
case
when buf in ('\') then
varbuf := varbuf || buf || buf; v_count := v_count + 2;
indx := indx + 1;
buf := next_char(indx, jsrc);
when buf in ('"', '/') then
varbuf := varbuf || buf; v_count := v_count + 1;
indx := indx + 1;
buf := next_char(indx, jsrc);
when buf = '''' then
if (json_strict = false) then
varbuf := varbuf || buf; v_count := v_count + 1;
indx := indx + 1;
buf := next_char(indx, jsrc);
else
s_error('strictmode - expected: " \ / b f n r t u ', tok);
end if;
when buf in ('b', 'f', 'n', 'r', 't') then
--backspace b = U+0008
--formfeed f = U+000C
--newline n = U+000A
--carret r = U+000D
--tabulator t = U+0009
case buf
when 'b' then varbuf := varbuf || chr(8);
when 'f' then varbuf := varbuf || chr(12);
when 'n' then varbuf := varbuf || chr(10);
when 'r' then varbuf := varbuf || chr(13);
when 't' then varbuf := varbuf || chr(9);
end case;
--varbuf := varbuf || buf;
v_count := v_count + 1;
indx := indx + 1;
buf := next_char(indx, jsrc);
when buf = 'u' then
--four hexadecimal chars
declare
four varchar2(4);
begin
four := next_char2(indx+1, jsrc, 4);
wrong := FALSE;
if (upper(substr(four, 1, 1)) not in ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f')) then wrong := TRUE; end if;
if (upper(substr(four, 2, 1)) not in ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f')) then wrong := TRUE; end if;
if (upper(substr(four, 3, 1)) not in ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f')) then wrong := TRUE; end if;
if (upper(substr(four, 4, 1)) not in ('0','1','2','3','4','5','6','7','8','9','A','B','C','D','E','F','a','b','c','d','e','f')) then wrong := TRUE; end if;
if (wrong) then
s_error('expected: " \u([0-9][A-F]){4}', tok);
end if;
-- varbuf := varbuf || buf || four;
varbuf := varbuf || '\'||four;--chr(to_number(four,'XXXX'));
v_count := v_count + 5;
indx := indx + 5;
buf := next_char(indx, jsrc);
end;
else
s_error('expected: " \ / b f n r t u ', tok);
end case;
else
varbuf := varbuf || buf; v_count := v_count + 1;
indx := indx + 1;
buf := next_char(indx, jsrc);
end if;
end loop;
if (buf is null) then
s_error('string ending not found', tok);
--debug('Premature string ending');
end if;
--debug(varbuf);
--dbms_output.put_line(varbuf);
if (v_extended is not null) then
updateClob(v_extended, unistr(varbuf));
tok.data_overflow := v_extended;
-- tok.data := dbms_lob.substr(v_extended, 1, 32767);
/* may read less than "max_string_chars" characters but it's a sample so doesn't matter */
dbms_lob.read(v_extended, max_string_chars, 1, tok.data);
else
tok.data := unistr(varbuf);
end if;
return indx;
end lexString;
/* scanner tokens:
'{', '}', ',', ':', '[', ']', STRING, NUMBER, TRUE, FALSE, NULL
*/
function lexer(jsrc in out nocopy json_src) return lTokens as
tokens lTokens;
indx pls_integer := 1;
tok_indx pls_integer := 1;
buf varchar2(4);
lin_no number := 1;
col_no number := 0;
begin
while (indx <= jsrc.len) loop
--read into buf
buf := next_char(indx, jsrc);
col_no := col_no + 1;
--convert to switch case
case
when buf = '{' then tokens(tok_indx) := mt('{', lin_no, col_no, null); tok_indx := tok_indx + 1;
when buf = '}' then tokens(tok_indx) := mt('}', lin_no, col_no, null); tok_indx := tok_indx + 1;
when buf = ',' then tokens(tok_indx) := mt(',', lin_no, col_no, null); tok_indx := tok_indx + 1;
when buf = ':' then tokens(tok_indx) := mt(':', lin_no, col_no, null); tok_indx := tok_indx + 1;
when buf = '[' then tokens(tok_indx) := mt('[', lin_no, col_no, null); tok_indx := tok_indx + 1;
when buf = ']' then tokens(tok_indx) := mt(']', lin_no, col_no, null); tok_indx := tok_indx + 1;
when buf = 't' then
if (next_char2(indx, jsrc, 4) != 'true') then
if (json_strict = false and REGEXP_LIKE(buf, '^[[:alpha:]]$', 'i')) then
tokens(tok_indx) := mt('STRING', lin_no, col_no, null);
indx := lexName(jsrc, tokens(tok_indx), indx);
col_no := col_no + length(tokens(tok_indx).data) + 1;
tok_indx := tok_indx + 1;
else
s_error('Expected: ''true''', lin_no, col_no);
end if;
else
tokens(tok_indx) := mt('TRUE', lin_no, col_no, null); tok_indx := tok_indx + 1;
indx := indx + 3;
col_no := col_no + 3;
end if;
when buf = 'n' then
if (next_char2(indx, jsrc, 4) != 'null') then
if (json_strict = false and REGEXP_LIKE(buf, '^[[:alpha:]]$', 'i')) then
tokens(tok_indx) := mt('STRING', lin_no, col_no, null);
indx := lexName(jsrc, tokens(tok_indx), indx);
col_no := col_no + length(tokens(tok_indx).data) + 1;
tok_indx := tok_indx + 1;
else
s_error('Expected: ''null''', lin_no, col_no);
end if;
else
tokens(tok_indx) := mt('NULL', lin_no, col_no, null); tok_indx := tok_indx + 1;
indx := indx + 3;
col_no := col_no + 3;
end if;
when buf = 'f' then
if (next_char2(indx, jsrc, 5) != 'false') then
if (json_strict = false and REGEXP_LIKE(buf, '^[[:alpha:]]$', 'i')) then
tokens(tok_indx) := mt('STRING', lin_no, col_no, null);
indx := lexName(jsrc, tokens(tok_indx), indx);
col_no := col_no + length(tokens(tok_indx).data) + 1;
tok_indx := tok_indx + 1;
else
s_error('Expected: ''false''', lin_no, col_no);
end if;
else
tokens(tok_indx) := mt('FALSE', lin_no, col_no, null); tok_indx := tok_indx + 1;
indx := indx + 4;
col_no := col_no + 4;
end if;
/* -- 9 = TAB, 10 = \n, 13 = \r (Linux = \n, Windows = \r\n, Mac = \r */
when (buf = Chr(10)) then --linux newlines
lin_no := lin_no + 1;
col_no := 0;
when (buf = Chr(13)) then --Windows or Mac way
lin_no := lin_no + 1;
col_no := 0;
if (jsrc.len >= indx+1) then -- better safe than sorry
buf := next_char(indx+1, jsrc);
if (buf = Chr(10)) then --\r\n
indx := indx + 1;
end if;
end if;
when (buf = CHR(9)) then null; --tabbing
when (buf in ('-', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9')) then --number
tokens(tok_indx) := mt('NUMBER', lin_no, col_no, null);
indx := lexNumber(jsrc, tokens(tok_indx), indx)-1;
col_no := col_no + length(tokens(tok_indx).data);
tok_indx := tok_indx + 1;
when buf = '"' then --string
tokens(tok_indx) := mt('STRING', lin_no, col_no, null);
indx := lexString(jsrc, tokens(tok_indx), indx, '"');
col_no := col_no + length(tokens(tok_indx).data) + 1;
tok_indx := tok_indx + 1;
when buf = '''' and json_strict = false then --string
tokens(tok_indx) := mt('STRING', lin_no, col_no, null);
indx := lexString(jsrc, tokens(tok_indx), indx, '''');
col_no := col_no + length(tokens(tok_indx).data) + 1; --hovsa her
tok_indx := tok_indx + 1;
when json_strict = false and REGEXP_LIKE(buf, '^[[:alpha:]]$', 'i') then
tokens(tok_indx) := mt('STRING', lin_no, col_no, null);
indx := lexName(jsrc, tokens(tok_indx), indx);
if (tokens(tok_indx).data_overflow is not null) then
/* use of lengthcc, so works correctly for 4-byte unicode characters (issue #169) */
col_no := col_no + lengthcc(tokens(tok_indx).data_overflow) + 1; --dbms_lob.getlength(tokens(tok_indx).data_overflow) + 1;
else
col_no := col_no + length(tokens(tok_indx).data) + 1;
end if;
tok_indx := tok_indx + 1;
when json_strict = false and buf||next_char(indx+1, jsrc) = '/*' then --strip comments
declare
saveindx number := indx;
un_esc clob;
begin
indx := indx + 1;
loop
indx := indx + 1;
buf := next_char(indx, jsrc)||next_char(indx+1, jsrc);
exit when buf = '*/';
exit when buf is null;
end loop;
if (indx = saveindx+2) then
--enter unescaped mode
--dbms_output.put_line('Entering unescaped mode');
un_esc := empty_clob();
dbms_lob.createtemporary(un_esc, true);
indx := indx + 1;
loop
indx := indx + 1;
buf := next_char(indx, jsrc)||next_char(indx+1, jsrc)||next_char(indx+2, jsrc)||next_char(indx+3, jsrc);
exit when buf = '/**/';
if buf is null then
s_error('Unexpected sequence /**/ to end unescaped data: '||buf, lin_no, col_no);
end if;
buf := next_char(indx, jsrc);
/* use of length2, so works correctly for 4-byte unicode characters (issue #169) */
dbms_lob.writeappend(un_esc, length2(buf), buf);
end loop;
tokens(tok_indx) := mt('ESTRING', lin_no, col_no, null);
tokens(tok_indx).data_overflow := un_esc;
/* use of lengthcc, so works correctly for 4-byte unicode characters (issue #169) */
col_no := col_no + lengthcc(un_esc) + 1; --dbms_lob.getlength(un_esc) + 1; --note: line count won't work properly
tok_indx := tok_indx + 1;
indx := indx + 2;
end if;
indx := indx + 1;
end;
when buf = ' ' then null; --space
else
s_error('Unexpected char: '||buf, lin_no, col_no);
end case;
indx := indx + 1;
end loop;
return tokens;
end lexer;
/* SCANNER END */
/* PARSER FUNCTIONS START */
procedure p_error(text varchar2, tok rToken) as
begin
raise_application_error(-20101, 'JSON Parser exception @ line: '||tok.line||' column: '||tok.col||' - '||text);
end;
function parseArr(tokens lTokens, indx in out nocopy pls_integer) return pljson_list as
e_arr pljson_element_array := pljson_element_array();
ret_list pljson_list := pljson_list();
v_count number := 0;
tok rToken;
pv pljson_number;
begin
--value, value, value ]
if (indx > tokens.count) then p_error('more elements in array was excepted', tok); end if;
tok := tokens(indx);
while (tok.type_name != ']') loop
e_arr.extend;
v_count := v_count + 1;
case tok.type_name
when 'TRUE' then e_arr(v_count) := pljson_bool(true);
when 'FALSE' then e_arr(v_count) := pljson_bool(false);
when 'NULL' then e_arr(v_count) := pljson_null();
when 'STRING' then e_arr(v_count) := case when tok.data_overflow is not null then pljson_string(tok.data_overflow) else pljson_string(tok.data) end;
when 'ESTRING' then e_arr(v_count) := pljson_string(tok.data_overflow, false);
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
--when 'NUMBER' then e_arr(v_count) := pljson_number(to_number(replace(tok.data, '.', decimalpoint)));
when 'NUMBER' then
pv := pljson_number(0);
pv.parse_number(replace(tok.data, '.', decimalpoint));
e_arr(v_count) := pv;
when '[' then
declare e_list pljson_list; begin
indx := indx + 1;
e_list := parseArr(tokens, indx);
e_arr(v_count) := e_list;
end;
when '{' then
indx := indx + 1;
e_arr(v_count) := parseObj(tokens, indx);
else
p_error('Expected a value', tok);
end case;
indx := indx + 1;
if (indx > tokens.count) then p_error('] not found', tok); end if;
tok := tokens(indx);
if (tok.type_name = ',') then --advance
indx := indx + 1;
if (indx > tokens.count) then p_error('more elements in array was excepted', tok); end if;
tok := tokens(indx);
if (tok.type_name = ']') then --premature exit
p_error('Premature exit in array', tok);
end if;
elsif (tok.type_name != ']') then --error
p_error('Expected , or ]', tok);
end if;
end loop;
ret_list.list_data := e_arr;
return ret_list;
end parseArr;
function parseMem(tokens lTokens, indx in out pls_integer, mem_name varchar2, mem_indx number) return pljson_element as
mem pljson_element;
tok rToken;
pv pljson_number;
begin
tok := tokens(indx);
case tok.type_name
when 'TRUE' then mem := pljson_bool(true);
when 'FALSE' then mem := pljson_bool(false);
when 'NULL' then mem := pljson_null();
when 'STRING' then mem := case when tok.data_overflow is not null then pljson_string(tok.data_overflow) else pljson_string(tok.data) end;
when 'ESTRING' then mem := pljson_string(tok.data_overflow, false);
/* E.I.Sarmas (github.com/dsnz) 2016-12-01 support for binary_double numbers */
--when 'NUMBER' then mem := pljson_number(to_number(replace(tok.data, '.', decimalpoint)));
when 'NUMBER' then
pv := pljson_number(0);
pv.parse_number(replace(tok.data, '.', decimalpoint));
mem := pv;
when '[' then
declare
e_list pljson_list;
begin
indx := indx + 1;
e_list := parseArr(tokens, indx);
mem := e_list;
end;
when '{' then
indx := indx + 1;
mem := parseObj(tokens, indx);
else
p_error('Found '||tok.type_name, tok);
end case;
mem.mapname := mem_name;
mem.mapindx := mem_indx;
indx := indx + 1;
return mem;
end parseMem;
/*procedure test_duplicate_members(arr in json_member_array, mem_name in varchar2, wheretok rToken) as
begin
for i in 1 .. arr.count loop
if (arr(i).member_name = mem_name) then
p_error('Duplicate member name', wheretok);
end if;
end loop;
end test_duplicate_members;*/
function parseObj(tokens lTokens, indx in out nocopy pls_integer) return pljson as
type memmap is table of number index by varchar2(4000); -- i've read somewhere that this is not possible - but it is!
mymap memmap;
nullelemfound boolean := false;
obj pljson;
tok rToken;
mem_name varchar(4000);
arr pljson_element_array := pljson_element_array();
begin
--what to expect?
while (indx <= tokens.count) loop
tok := tokens(indx);
--debug('E: '||tok.type_name);
case tok.type_name
when 'STRING' then
--member
mem_name := substr(tok.data, 1, 4000);
begin
if (mem_name is null) then
if (nullelemfound) then
p_error('Duplicate empty member: ', tok);
else
nullelemfound := true;
end if;
elsif (mymap(mem_name) is not null) then
p_error('Duplicate member name: '||mem_name, tok);
end if;
exception
when no_data_found then mymap(mem_name) := 1;
end;
indx := indx + 1;
if (indx > tokens.count) then p_error('Unexpected end of input', tok); end if;
tok := tokens(indx);
indx := indx + 1;
if (indx > tokens.count) then p_error('Unexpected end of input', tok); end if;
if (tok.type_name = ':') then
--parse
declare
jmb pljson_element;
x number;
begin
x := arr.count + 1;
jmb := parseMem(tokens, indx, mem_name, x);
arr.extend;
arr(x) := jmb;
end;
else
p_error('Expected '':''', tok);
end if;
--move indx forward if ',' is found
if (indx > tokens.count) then p_error('Unexpected end of input', tok); end if;
tok := tokens(indx);
if (tok.type_name = ',') then
--debug('found ,');
indx := indx + 1;
tok := tokens(indx);
if (tok.type_name = '}') then --premature exit
p_error('Premature exit in json object', tok);
end if;
elsif (tok.type_name != '}') then
p_error('A comma seperator is probably missing', tok);
end if;
when '}' then
obj := pljson();
obj.json_data := arr;
return obj;
else
p_error('Expected string or }', tok);
end case;
end loop;
p_error('} not found', tokens(indx-1));
return obj;
end;
function parser(str varchar2) return pljson as
tokens lTokens;
obj pljson;
indx pls_integer := 1;
jsrc json_src;
begin
update_decimalpoint();
jsrc := prepareVarchar2(str);
tokens := lexer(jsrc);
if (tokens(indx).type_name = '{') then
indx := indx + 1;
obj := parseObj(tokens, indx);
else
raise_application_error(-20101, 'JSON Parser exception - no { start found');
end if;
if (tokens.count != indx) then
p_error('} should end the JSON object', tokens(indx));
end if;
return obj;
end parser;
function parse_list(str varchar2) return pljson_list as
tokens lTokens;
obj pljson_list;
indx pls_integer := 1;
jsrc json_src;
begin
update_decimalpoint();
jsrc := prepareVarchar2(str);
tokens := lexer(jsrc);
if (tokens(indx).type_name = '[') then
indx := indx + 1;
obj := parseArr(tokens, indx);
else
raise_application_error(-20101, 'JSON List Parser exception - no [ start found');
end if;
if (tokens.count != indx) then
p_error('] should end the JSON List object', tokens(indx));
end if;
return obj;
end parse_list;
function parse_list(str clob) return pljson_list as
tokens lTokens;
obj pljson_list;
indx pls_integer := 1;
jsrc json_src;
begin
update_decimalpoint();
jsrc := prepareClob(str);
tokens := lexer(jsrc);
if (tokens(indx).type_name = '[') then
indx := indx + 1;
obj := parseArr(tokens, indx);
else
raise_application_error(-20101, 'JSON List Parser exception - no [ start found');
end if;
if (tokens.count != indx) then
p_error('] should end the JSON List object', tokens(indx));
end if;
return obj;
end parse_list;
function parser(str clob) return pljson as
tokens lTokens;
obj pljson;
indx pls_integer := 1;
jsrc json_src;
begin
update_decimalpoint();
--dbms_output.put_line('Using clob');
jsrc := prepareClob(str);
tokens := lexer(jsrc);
if (tokens(indx).type_name = '{') then
indx := indx + 1;
obj := parseObj(tokens, indx);
else
raise_application_error(-20101, 'JSON Parser exception - no { start found');
end if;
if (tokens.count != indx) then
p_error('} should end the JSON object', tokens(indx));
end if;
return obj;
end parser;
function parse_any(str varchar2) return pljson_element as
tokens lTokens;
obj pljson_list;
ret pljson_element;
indx pls_integer := 1;
jsrc json_src;
begin
update_decimalpoint();
jsrc := prepareVarchar2(str);
tokens := lexer(jsrc);
tokens(tokens.count+1).type_name := ']';
obj := parseArr(tokens, indx);
if (tokens.count != indx) then
p_error('] should end the JSON List object', tokens(indx));
end if;
return obj.head();
end parse_any;
function parse_any(str clob) return pljson_element as
tokens lTokens;
obj pljson_list;
indx pls_integer := 1;
jsrc json_src;
begin
update_decimalpoint();
jsrc := prepareClob(str);
tokens := lexer(jsrc);
tokens(tokens.count+1).type_name := ']';
obj := parseArr(tokens, indx);
if (tokens.count != indx) then
p_error('] should end the JSON List object', tokens(indx));
end if;
return obj.head();
end parse_any;
/* last entry is the one to keep */
procedure remove_duplicates(obj in out nocopy pljson) as
type memberlist is table of pljson_element index by varchar2(4000);
members memberlist;
nulljsonvalue pljson_element := null;
validated pljson := pljson();
indx varchar2(4000);
begin
for i in 1 .. obj.count loop
if (obj.get(i).mapname is null) then
nulljsonvalue := obj.get(i);
else
members(obj.get(i).mapname) := obj.get(i);
end if;
end loop;
validated.check_duplicate(false);
indx := members.first;
loop
exit when indx is null;
validated.put(indx, members(indx));
indx := members.next(indx);
end loop;
if (nulljsonvalue is not null) then
validated.put('', nulljsonvalue);
end if;
validated.check_for_duplicate := obj.check_for_duplicate;
obj := validated;
end;
function get_version return varchar2 as
begin
return 'PL/JSON 3.5.2';
end get_version;
end pljson_parser;```

View File

@@ -0,0 +1,727 @@
# PLJSON_PRINTER
## Package Specification
```sql
package pljson_printer as
/*
Copyright (c) 2010 Jonas Krogsboell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
indent_string varchar2(10 char) := ' '; --chr(9); for tab
newline_char varchar2(2 char) := chr(13)||chr(10); -- Windows style
--newline_char varchar2(2) := chr(10); -- Mac style
--newline_char varchar2(2) := chr(13); -- Linux style
ascii_output boolean not null := true;
empty_string_as_null boolean not null := false;
escape_solidus boolean not null := false;
function pretty_print(obj pljson, spaces boolean default true, line_length number default 0) return varchar2;
function pretty_print_list(obj pljson_list, spaces boolean default true, line_length number default 0) return varchar2;
function pretty_print_any(json_part pljson_element, spaces boolean default true, line_length number default 0) return varchar2;
procedure pretty_print(obj pljson, spaces boolean default true, buf in out nocopy clob, line_length number default 0, erase_clob boolean default true);
procedure pretty_print_list(obj pljson_list, spaces boolean default true, buf in out nocopy clob, line_length number default 0, erase_clob boolean default true);
procedure pretty_print_any(json_part pljson_element, spaces boolean default true, buf in out nocopy clob, line_length number default 0, erase_clob boolean default true);
procedure dbms_output_clob(my_clob clob, delim varchar2, jsonp varchar2 default null);
procedure htp_output_clob(my_clob clob, jsonp varchar2 default null);
-- made public just for testing/profiling...
function escapeString(str varchar2) return varchar2;
end pljson_printer;```
## Package Body
```sql
package body pljson_printer as
max_line_len number := 0;
cur_line_len number := 0;
-- associative array used inside escapeString to cache the escaped version of every character
-- escaped so far (example: char_map('"') contains the '\"' string)
-- (if the character does not need to be escaped, the character is stored unchanged in the array itself)
-- type Rmap_char is record(buf varchar2(40), len integer);
type Tmap_char_string is table of varchar2(40) index by varchar2(1 char); /* index by unicode char */
char_map Tmap_char_string;
-- since char_map the associative array is a global variable reused across multiple calls to escapeString,
-- i need to be able to detect that the escape_solidus or ascii_output global parameters have been changed,
-- in order to clear it and avoid using escape sequences that have been cached using the previous values
char_map_escape_solidus boolean := escape_solidus;
char_map_ascii_output boolean := ascii_output;
function llcheck(str in varchar2) return varchar2 as
begin
--dbms_output.put_line(cur_line_len || ' : ' || str);
if (max_line_len > 0 and length(str)+cur_line_len > max_line_len) then
cur_line_len := length(str);
return newline_char || str;
else
cur_line_len := cur_line_len + length(str);
return str;
end if;
end llcheck;
-- escapes a single character.
function escapeChar(ch char) return varchar2 deterministic is
result varchar2(20);
begin
--backspace b = U+0008
--formfeed f = U+000C
--newline n = U+000A
--carret r = U+000D
--tabulator t = U+0009
result := ch;
case ch
when chr( 8) then result := '\b';
when chr( 9) then result := '\t';
when chr(10) then result := '\n';
when chr(12) then result := '\f';
when chr(13) then result := '\r';
when chr(34) then result := '\"';
when chr(47) then if (escape_solidus) then result := '\/'; end if;
when chr(92) then result := '\\';
/* WARNING: ascii() returns PLS_INTEGER and large unicode code points can be negative */
else if (ascii(ch) >= 0 and ascii(ch) < 32) then
result := '\u' || replace(substr(to_char(ascii(ch), 'XXXX'), 2, 4), ' ', '0');
elsif (ascii_output) then
result := replace(asciistr(ch), '\', '\u');
end if;
end case;
return result;
end;
function escapeString(str varchar2) return varchar2 as
sb varchar2(32767 byte) := '';
buf varchar2(40);
ch varchar2(1 char); /* unicode char */
begin
if (str is null) then return ''; end if;
-- clear the cache if global parameters have been changed
if char_map_escape_solidus <> escape_solidus or
char_map_ascii_output <> ascii_output
then
char_map.delete;
char_map_escape_solidus := escape_solidus;
char_map_ascii_output := ascii_output;
end if;
for i in 1 .. length(str) loop
ch := substr(str, i, 1 ) ;
begin
-- it this char has already been processed, I have cached its escaped value
buf:=char_map(ch);
exception when no_Data_found then
-- otherwise, i convert the value and add it to the cache
buf := escapeChar(ch);
char_map(ch) := buf;
end;
sb := sb || buf;
end loop;
return sb;
end escapeString;
function newline(spaces boolean) return varchar2 as
begin
cur_line_len := 0;
if (spaces) then return newline_char; else return ''; end if;
end;
/* function get_schema return varchar2 as
begin
return sys_context('userenv', 'current_schema');
end;
*/
function tab(indent number, spaces boolean) return varchar2 as
i varchar(200) := '';
begin
if (not spaces) then return ''; end if;
for x in 1 .. indent loop i := i || indent_string; end loop;
return i;
end;
function getCommaSep(spaces boolean) return varchar2 as
begin
if (spaces) then return ', '; else return ','; end if;
end;
function getMemName(mem pljson_element, spaces boolean) return varchar2 as
begin
if (spaces) then
return llcheck('"'||escapeString(mem.mapname)||'"') || llcheck(' : ');
else
return llcheck('"'||escapeString(mem.mapname)||'"') || llcheck(':');
end if;
end;
/* Clob method start here */
procedure add_to_clob(buf_lob in out nocopy clob, buf_str in out nocopy varchar2, str varchar2) as
begin
-- if (length(str) > 5000 - length(buf_str)) then
if (lengthb(str) > 32767 - lengthb(buf_str)) then
-- dbms_lob.writeappend(buf_lob, length2(buf_str), buf_str);
dbms_lob.append(buf_lob, buf_str);
buf_str := str;
else
buf_str := buf_str || str;
end if;
end add_to_clob;
procedure flush_clob(buf_lob in out nocopy clob, buf_str in out nocopy varchar2) as
begin
-- dbms_lob.writeappend(buf_lob, length2(buf_str), buf_str);
dbms_lob.append(buf_lob, buf_str);
end flush_clob;
procedure ppObj(obj pljson, indent number, buf in out nocopy clob, spaces boolean, buf_str in out nocopy varchar2);
procedure ppString(elem pljson_string, buf in out nocopy clob, buf_str in out nocopy varchar2) is
offset number := 1;
/* E.I.Sarmas (github.com/dsnz) 2016-01-21 limit to 5000 chars */
v_str varchar(5000 char);
amount number := 5000; /* chunk size for use in escapeString, less than this number may be copied */
begin
if empty_string_as_null and elem.extended_str is null and elem.str is null then
add_to_clob(buf, buf_str, 'null');
else
add_to_clob(buf, buf_str, case when elem.num = 1 then '"' else '/**/' end);
if (elem.extended_str is not null) then --clob implementation
while (offset <= dbms_lob.getlength(elem.extended_str)) loop
dbms_lob.read(elem.extended_str, amount, offset, v_str);
if (elem.num = 1) then
add_to_clob(buf, buf_str, escapeString(v_str));
else
add_to_clob(buf, buf_str, v_str);
end if;
offset := offset + amount;
end loop;
else
if (elem.num = 1) then
while (offset <= length(elem.str)) loop
v_str:=substr(elem.str, offset, amount);
add_to_clob(buf, buf_str, escapeString(v_str));
offset := offset + amount;
end loop;
else
add_to_clob(buf, buf_str, elem.str);
end if;
end if;
add_to_clob(buf, buf_str, case when elem.num = 1 then '"' else '/**/' end);
end if;
end;
procedure ppEA(input pljson_list, indent number, buf in out nocopy clob, spaces boolean, buf_str in out nocopy varchar2) as
elem pljson_element;
arr pljson_element_array := input.list_data;
numbuf varchar2(4000);
begin
for y in 1 .. arr.count loop
elem := arr(y);
if (elem is not null) then
case elem.typeval
/* number */
when 4 then
numbuf := treat(elem as pljson_number).number_toString();
add_to_clob(buf, buf_str, llcheck(numbuf));
/* string */
when 3 then
ppString(treat(elem as pljson_string), buf, buf_str);
/* bool */
when 5 then
if (elem.get_bool()) then
add_to_clob(buf, buf_str, llcheck('true'));
else
add_to_clob(buf, buf_str, llcheck('false'));
end if;
/* null */
when 6 then
add_to_clob(buf, buf_str, llcheck('null'));
/* array */
when 2 then
add_to_clob(buf, buf_str, llcheck('['));
ppEA(treat(elem as pljson_list), indent, buf, spaces, buf_str);
add_to_clob(buf, buf_str, llcheck(']'));
/* object */
when 1 then
ppObj(treat(elem as pljson), indent, buf, spaces, buf_str);
else
add_to_clob(buf, buf_str, llcheck(elem.get_type));
end case;
end if;
if (y != arr.count) then add_to_clob(buf, buf_str, llcheck(getCommaSep(spaces))); end if;
end loop;
end ppEA;
procedure ppMem(mem pljson_element, indent number, buf in out nocopy clob, spaces boolean, buf_str in out nocopy varchar2) as
numbuf varchar2(4000);
begin
add_to_clob(buf, buf_str, llcheck(tab(indent, spaces)) || llcheck(getMemName(mem, spaces)));
case mem.typeval
/* number */
when 4 then
numbuf := treat(mem as pljson_number).number_toString();
add_to_clob(buf, buf_str, llcheck(numbuf));
/* string */
when 3 then
ppString(treat(mem as pljson_string), buf, buf_str);
/* bool */
when 5 then
if (mem.get_bool()) then
add_to_clob(buf, buf_str, llcheck('true'));
else
add_to_clob(buf, buf_str, llcheck('false'));
end if;
/* null */
when 6 then
add_to_clob(buf, buf_str, llcheck('null'));
/* array */
when 2 then
add_to_clob(buf, buf_str, llcheck('['));
ppEA(treat(mem as pljson_list), indent, buf, spaces, buf_str);
add_to_clob(buf, buf_str, llcheck(']'));
/* object */
when 1 then
ppObj(treat(mem as pljson), indent, buf, spaces, buf_str);
else
add_to_clob(buf, buf_str, llcheck(mem.get_type));
end case;
end ppMem;
procedure ppObj(obj pljson, indent number, buf in out nocopy clob, spaces boolean, buf_str in out nocopy varchar2) as
begin
add_to_clob(buf, buf_str, llcheck('{') || newline(spaces));
for m in 1 .. obj.json_data.count loop
ppMem(obj.json_data(m), indent+1, buf, spaces, buf_str);
if (m != obj.json_data.count) then
add_to_clob(buf, buf_str, llcheck(',') || newline(spaces));
else
add_to_clob(buf, buf_str, newline(spaces));
end if;
end loop;
add_to_clob(buf, buf_str, llcheck(tab(indent, spaces)) || llcheck('}')); -- || chr(13);
end ppObj;
procedure pretty_print(obj pljson, spaces boolean default true, buf in out nocopy clob, line_length number default 0, erase_clob boolean default true) as
buf_str varchar2(32767);
amount number := dbms_lob.getlength(buf);
begin
if (erase_clob and amount > 0) then
dbms_lob.trim(buf, 0);
-- dbms_lob.erase(buf, amount);
end if;
max_line_len := line_length;
cur_line_len := 0;
ppObj(obj, 0, buf, spaces, buf_str);
flush_clob(buf, buf_str);
end;
procedure pretty_print_list(obj pljson_list, spaces boolean default true, buf in out nocopy clob, line_length number default 0, erase_clob boolean default true) as
buf_str varchar2(32767);
amount number := dbms_lob.getlength(buf);
begin
if (erase_clob and amount > 0) then
dbms_lob.trim(buf, 0);
-- dbms_lob.erase(buf, amount);
end if;
max_line_len := line_length;
cur_line_len := 0;
add_to_clob(buf, buf_str, llcheck('['));
ppEA(obj, 0, buf, spaces, buf_str);
add_to_clob(buf, buf_str, llcheck(']'));
flush_clob(buf, buf_str);
end;
procedure pretty_print_any(json_part pljson_element, spaces boolean default true, buf in out nocopy clob, line_length number default 0, erase_clob boolean default true) as
buf_str varchar2(32767) := '';
numbuf varchar2(4000);
amount number := dbms_lob.getlength(buf);
begin
if (erase_clob and amount > 0) then
dbms_lob.trim(buf, 0);
-- dbms_lob.erase(buf, amount);
end if;
case json_part.typeval
/* number */
when 4 then
numbuf := treat(json_part as pljson_number).number_toString();
add_to_clob(buf, buf_str, numbuf);
/* string */
when 3 then
ppString(treat(json_part as pljson_string), buf, buf_str);
/* bool */
when 5 then
if (json_part.get_bool()) then
add_to_clob(buf, buf_str, 'true');
else
add_to_clob(buf, buf_str, 'false');
end if;
/* null */
when 6 then
add_to_clob(buf, buf_str, 'null');
/* array */
when 2 then
pretty_print_list(pljson_list(json_part), spaces, buf, line_length);
return;
/* object */
when 1 then
pretty_print(pljson(json_part), spaces, buf, line_length);
return;
else
add_to_clob(buf, buf_str, 'unknown type:' || json_part.get_type);
end case;
flush_clob(buf, buf_str);
end;
/* Clob method end here */
/* Varchar2 method start here */
procedure add_buf (buf in out nocopy varchar2, str in varchar2) as
begin
if (lengthb(str)>32767-lengthb(buf)) then
raise_application_error(-20001,'Length of result JSON more than 32767 bytes. Use to_clob() procedures');
end if;
buf := buf || str;
end;
procedure ppString(elem pljson_string, buf in out nocopy varchar2) is
offset number := 1;
/* E.I.Sarmas (github.com/dsnz) 2016-01-21 limit to 5000 chars */
v_str varchar(5000 char);
amount number := 5000; /* chunk size for use in escapeString, less than this number may be copied */
begin
if empty_string_as_null and elem.extended_str is null and elem.str is null then
add_buf(buf, 'null');
else
add_buf(buf, case when elem.num = 1 then '"' else '/**/' end);
if (elem.extended_str is not null) then --clob implementation
while (offset <= dbms_lob.getlength(elem.extended_str)) loop
dbms_lob.read(elem.extended_str, amount, offset, v_str);
if (elem.num = 1) then
add_buf(buf, escapeString(v_str));
else
add_buf(buf, v_str);
end if;
offset := offset + amount;
end loop;
else
if (elem.num = 1) then
while (offset <= length(elem.str)) loop
v_str:=substr(elem.str, offset, amount);
add_buf(buf, escapeString(v_str));
offset := offset + amount;
end loop;
else
add_buf(buf, elem.str);
end if;
end if;
add_buf(buf, case when elem.num = 1 then '"' else '/**/' end);
end if;
end;
procedure ppObj(obj pljson, indent number, buf in out nocopy varchar2, spaces boolean);
procedure ppEA(input pljson_list, indent number, buf in out varchar2, spaces boolean) as
elem pljson_element;
arr pljson_element_array := input.list_data;
str varchar2(400);
begin
for y in 1 .. arr.count loop
elem := arr(y);
if (elem is not null) then
case elem.typeval
/* number */
when 4 then
str := treat(elem as pljson_number).number_toString();
add_buf(buf, llcheck(str));
/* string */
when 3 then
ppString(treat(elem as pljson_string), buf);
/* bool */
when 5 then
if (elem.get_bool()) then
add_buf (buf, llcheck('true'));
else
add_buf (buf, llcheck('false'));
end if;
/* null */
when 6 then
add_buf (buf, llcheck('null'));
/* array */
when 2 then
add_buf( buf, llcheck('['));
ppEA(treat(elem as pljson_list), indent, buf, spaces);
add_buf( buf, llcheck(']'));
/* object */
when 1 then
ppObj(treat(elem as pljson), indent, buf, spaces);
else
add_buf (buf, llcheck(elem.get_type)); /* should never happen */
end case;
end if;
if (y != arr.count) then add_buf(buf, llcheck(getCommaSep(spaces))); end if;
end loop;
end ppEA;
procedure ppMem(mem pljson_element, indent number, buf in out nocopy varchar2, spaces boolean) as
str varchar2(400) := '';
begin
add_buf(buf, llcheck(tab(indent, spaces)) || getMemName(mem, spaces));
case mem.typeval
/* number */
when 4 then
str := treat(mem as pljson_number).number_toString();
add_buf(buf, llcheck(str));
/* string */
when 3 then
ppString(treat(mem as pljson_string), buf);
/* bool */
when 5 then
if (mem.get_bool()) then
add_buf(buf, llcheck('true'));
else
add_buf(buf, llcheck('false'));
end if;
/* null */
when 6 then
add_buf(buf, llcheck('null'));
/* array */
when 2 then
add_buf(buf, llcheck('['));
ppEA(treat(mem as pljson_list), indent, buf, spaces);
add_buf(buf, llcheck(']'));
/* object */
when 1 then
ppObj(treat(mem as pljson), indent, buf, spaces);
else
add_buf(buf, llcheck(mem.get_type)); /* should never happen */
end case;
end ppMem;
procedure ppObj(obj pljson, indent number, buf in out nocopy varchar2, spaces boolean) as
begin
add_buf (buf, llcheck('{') || newline(spaces));
for m in 1 .. obj.json_data.count loop
ppMem(obj.json_data(m), indent+1, buf, spaces);
if (m != obj.json_data.count) then
add_buf(buf, llcheck(',') || newline(spaces));
else
add_buf(buf, newline(spaces));
end if;
end loop;
add_buf(buf, llcheck(tab(indent, spaces)) || llcheck('}')); -- || chr(13);
end ppObj;
function pretty_print(obj pljson, spaces boolean default true, line_length number default 0) return varchar2 as
buf varchar2(32767 byte) := '';
begin
max_line_len := line_length;
cur_line_len := 0;
ppObj(obj, 0, buf, spaces);
return buf;
end pretty_print;
function pretty_print_list(obj pljson_list, spaces boolean default true, line_length number default 0) return varchar2 as
buf varchar2(32767 byte) :='';
begin
max_line_len := line_length;
cur_line_len := 0;
add_buf(buf, llcheck('['));
ppEA(obj, 0, buf, spaces);
add_buf(buf, llcheck(']'));
return buf;
end;
function pretty_print_any(json_part pljson_element, spaces boolean default true, line_length number default 0) return varchar2 as
buf varchar2(32767) := '';
begin
case json_part.typeval
/* number */
when 4 then
buf := treat(json_part as pljson_number).number_toString();
/* string */
when 3 then
ppString(treat(json_part as pljson_string), buf);
/* bool */
when 5 then
if (json_part.get_bool()) then buf := 'true'; else buf := 'false'; end if;
/* null */
when 6 then
buf := 'null';
/* array */
when 2 then
buf := pretty_print_list(pljson_list(json_part), spaces, line_length);
/* object */
when 1 then
buf := pretty_print(pljson(json_part), spaces, line_length);
else
buf := 'weird error: ' || json_part.get_type;
end case;
return buf;
end;
procedure dbms_output_clob(my_clob clob, delim varchar2, jsonp varchar2 default null) as
prev number := 1;
indx number := 1;
size_of_nl number := length2(delim);
v_str varchar2(32767);
amount number;
max_string_chars number := 5000; /* chunk size, less than this number may be copied */
begin
if (jsonp is not null) then dbms_output.put_line(jsonp||'('); end if;
while (indx != 0) loop
--read every line
indx := dbms_lob.instr(my_clob, delim, prev+1);
--dbms_output.put_line(prev || ' to ' || indx);
if (indx = 0) then
--emit from prev to end;
amount := max_string_chars;
--dbms_output.put_line(' mycloblen ' || dbms_lob.getlength(my_clob));
loop
dbms_lob.read(my_clob, amount, prev, v_str);
dbms_output.put_line(v_str);
prev := prev+amount;
exit when prev >= dbms_lob.getlength(my_clob);
end loop;
else
amount := indx - prev;
if (amount > max_string_chars) then
amount := max_string_chars;
--dbms_output.put_line(' mycloblen ' || dbms_lob.getlength(my_clob));
loop
dbms_lob.read(my_clob, amount, prev, v_str);
dbms_output.put_line(v_str);
prev := prev+amount;
amount := indx - prev;
exit when prev >= indx - 1;
if (amount > max_string_chars) then
amount := max_string_chars;
end if;
end loop;
prev := indx + size_of_nl;
else
dbms_lob.read(my_clob, amount, prev, v_str);
dbms_output.put_line(v_str);
prev := indx + size_of_nl;
end if;
end if;
end loop;
if (jsonp is not null) then dbms_output.put_line(')'); end if;
/* while (amount != 0) loop
indx := dbms_lob.instr(my_clob, delim, prev+1);
-- dbms_output.put_line(prev || ' to ' || indx);
if (indx = 0) then
indx := dbms_lob.getlength(my_clob)+1;
end if;
if (indx-prev > 32767) then
indx := prev+32767;
end if;
-- dbms_output.put_line(prev || ' to ' || indx);
--substr doesnt work properly on all platforms! (come on oracle - error on Oracle VM for virtualbox)
-- dbms_output.put_line(dbms_lob.substr(my_clob, indx-prev, prev));
amount := indx-prev;
-- dbms_output.put_line('amount'||amount);
dbms_lob.read(my_clob, amount, prev, v_str);
dbms_output.put_line(v_str);
prev := indx+size_of_nl;
if (amount = 32767) then prev := prev-size_of_nl-1; end if;
end loop;
if (jsonp is not null) then dbms_output.put_line(')'); end if;*/
end;
/* procedure dbms_output_clob(my_clob clob, delim varchar2, jsonp varchar2 default null) as
prev number := 1;
indx number := 1;
size_of_nl number := length2(delim);
v_str varchar2(32767);
amount number;
begin
if (jsonp is not null) then dbms_output.put_line(jsonp||'('); end if;
while (indx != 0) loop
indx := dbms_lob.instr(my_clob, delim, prev+1);
--dbms_output.put_line(prev || ' to ' || indx);
if (indx-prev > 32767) then
indx := prev+32767;
end if;
--dbms_output.put_line(prev || ' to ' || indx);
--substr doesnt work properly on all platforms! (come on oracle - error on Oracle VM for virtualbox)
if (indx = 0) then
--dbms_output.put_line(dbms_lob.substr(my_clob, dbms_lob.getlength(my_clob)-prev+size_of_nl, prev));
amount := dbms_lob.getlength(my_clob)-prev+size_of_nl;
dbms_lob.read(my_clob, amount, prev, v_str);
else
--dbms_output.put_line(dbms_lob.substr(my_clob, indx-prev, prev));
amount := indx-prev;
--dbms_output.put_line('amount'||amount);
dbms_lob.read(my_clob, amount, prev, v_str);
end if;
dbms_output.put_line(v_str);
prev := indx+size_of_nl;
if (amount = 32767) then prev := prev-size_of_nl-1; end if;
end loop;
if (jsonp is not null) then dbms_output.put_line(')'); end if;
end;
*/
procedure htp_output_clob(my_clob clob, jsonp varchar2 default null) as
/*amount number := 4096;
pos number := 1;
len number;
*/
l_amt number default 4096;
l_off number default 1;
l_str varchar2(32000);
begin
if (jsonp is not null) then htp.prn(jsonp||'('); end if;
begin
loop
dbms_lob.read( my_clob, l_amt, l_off, l_str );
-- it is vital to use htp.PRN to avoid
-- spurious line feeds getting added to your
-- document
htp.prn( l_str );
l_off := l_off+l_amt;
end loop;
exception
when no_data_found then NULL;
end;
/*
len := dbms_lob.getlength(my_clob);
while (pos < len) loop
htp.prn(dbms_lob.substr(my_clob, amount, pos)); -- should I replace substr with dbms_lob.read?
--dbms_output.put_line(dbms_lob.substr(my_clob, amount, pos));
pos := pos + amount;
end loop;
*/
if (jsonp is not null) then htp.prn(')'); end if;
end;
end pljson_printer;```

204
docs/packages/PLJSON_UT.md Normal file
View File

@@ -0,0 +1,204 @@
# PLJSON_UT
## Package Specification
```sql
package pljson_ut as
/*
*
* E.I.Sarmas (github.com/dsnz) 2017-07-22
*
* Simple unit test framework for pljson
*
*/
suite_id number;
suite_name varchar2(100);
file_name varchar2(100);
pass_count number;
fail_count number;
total_count number;
case_name varchar2(100);
case_pass number;
case_fail number;
case_total number;
INDENT_1 varchar2(10) := ' ';
INDENT_2 varchar2(10) := ' ';
procedure testsuite(suite_name_ varchar2, file_name_ varchar2);
procedure testcase(case_name_ varchar2);
procedure pass(test_name varchar2 := null);
procedure fail(test_name varchar2 := null);
procedure assertTrue(b boolean, test_name varchar2 := null);
procedure assertFalse(b boolean, test_name varchar2 := null);
procedure testsuite_report;
procedure startup;
procedure shutdown;
end pljson_ut;```
## Package Body
```sql
package body pljson_ut as
/*
*
* E.I.Sarmas (github.com/dsnz) 2017-07-22
*
* Simple unit test framework for pljson
*
*/
procedure testsuite(suite_name_ varchar2, file_name_ varchar2) is
begin
suite_id := suite_id + 1;
suite_name := suite_name_;
file_name := file_name_;
pass_count := 0;
fail_count := 0;
total_count := 0;
dbms_output.put_line(suite_name_);
end;
procedure testcase(case_name_ varchar2) is
begin
case_name := case_name_;
case_pass := 0;
case_fail := 0;
case_total := 0;
dbms_output.put_line(INDENT_1 || case_name_);
end;
procedure pass(test_name varchar2 := null) is
begin
if (case_total = 0) then
pass_count := pass_count + 1;
total_count := total_count + 1;
end if;
case_pass := case_pass + 1;
case_total := case_total + 1;
if (test_name is not null) then
dbms_output.put_line(INDENT_2 || 'OK: '|| test_name);
end if;
end;
procedure fail(test_name varchar2 := null) is
begin
if (case_fail = 0) then
fail_count := fail_count + 1;
if (case_total = 0) then
total_count := total_count + 1;
else
pass_count := pass_count - 1;
end if;
end if;
case_fail := case_fail + 1;
case_total := case_total + 1;
if (test_name is not null) then
dbms_output.put_line(INDENT_2 || 'FAILED: '|| test_name);
end if;
end;
procedure assertTrue(b boolean, test_name varchar2 := null) is
begin
if (b) then
pass(test_name);
else
fail(test_name);
end if;
end;
procedure assertFalse(b boolean, test_name varchar2 := null) is
begin
if (not b) then
pass(test_name);
else
fail(test_name);
end if;
end;
procedure testsuite_report is
begin
dbms_output.put_line('');
dbms_output.put_line(
total_count || ' tests, '
|| pass_count || ' passed, '
|| fail_count || ' failed'
);
execute immediate 'insert into pljson_testsuite values (:1, :2, :3, :4, :5, :6)'
using suite_id, suite_name, file_name, pass_count, fail_count, total_count;
end;
procedure startup is
begin
suite_id := 0;
execute immediate 'truncate table pljson_testsuite';
end;
procedure shutdown is
begin
commit;
dbms_output.put_line('');
for rec in (
select suite_id, suite_name, passed, failed, total, file_name
from (
select 3 s, suite_id,
lpad(suite_name, 30) suite_name,
to_char(passed, '999999') passed,
to_char(failed, '999999') failed,
to_char(total, '999999') total,
lpad(file_name, 30) file_name
from pljson_testsuite
union
select 1 s, 0 suite_id,
lpad('SUITE_NAME', 30) suite_name,
lpad('PASSED', 7) passed,
lpad('FAILED', 7) failed,
lpad('TOTAL', 7) total,
lpad('FILE_NAME', 30) file_name
from dual
union
select 5 s, 0,
lpad('ALL TESTS', 30) suite_name,
to_char(sum(passed), '999999') passed,
to_char(sum(failed), '999999') failed,
to_char(sum(total), '999999') total,
lpad(' ', 30) file_name
from pljson_testsuite
union
select 2 s, 0 suite_id,
lpad('-', 30, '-') suite_name,
lpad('-', 7, '-') passed,
lpad('-', 7, '-') failed,
lpad('-', 7, '-') total,
lpad('-', 30, '-') file_name
from dual
union
select 4 s, 0 suite_id,
lpad('-', 30, '-') suite_name,
lpad('-', 7, '-') passed,
lpad('-', 7, '-') failed,
lpad('-', 7, '-') total,
lpad('-', 30, '-') file_name
from dual
order by s, suite_id
)
)
loop
dbms_output.put_line(
rec.suite_name||' '||rec.passed||' '||rec.failed||' '||rec.total||' '||rec.file_name
);
end loop;
end;
end pljson_ut;```

View File

@@ -0,0 +1,370 @@
# PLJSON_UTIL_PKG
## Package Specification
```sql
package pljson_util_pkg authid current_user as
/*
Purpose: JSON utilities for PL/SQL
see http://ora-00001.blogspot.com/
Remarks:
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
JKR 01.05.2010 Edited to fit in PL/JSON
JKR 19.01.2011 Newest stylesheet + bugfix handling
*/
-- generate JSON from REF Cursor
function ref_cursor_to_json (p_ref_cursor in sys_refcursor,
p_max_rows in number := null,
p_skip_rows in number := null) return pljson_list;
-- generate JSON from SQL statement
function sql_to_json (p_sql in varchar2,
p_max_rows in number := null,
p_skip_rows in number := null) return pljson_list;
end pljson_util_pkg;```
## Package Body
```sql
package body pljson_util_pkg as
scanner_exception exception;
pragma exception_init(scanner_exception, -20100);
parser_exception exception;
pragma exception_init(parser_exception, -20101);
/*
Purpose: JSON utilities for PL/SQL
Remarks:
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
*/
function get_xml_to_json_stylesheet return varchar2 as
stylesheet varchar2(32767);
nls_numeric_characters varchar2(2);
begin
/*
Purpose: return XSLT stylesheet for XML to JSON transformation
Remarks: see http://code.google.com/p/xml2json-xslt/
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
MBR 30.01.2010 Added fix for nulls
*/
stylesheet := q'^<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<!--
Copyright (c) 2006,2008 Doeke Zanstra
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. Redistributions in binary
form must reproduce the above copyright notice, this list of conditions and the
following disclaimer in the documentation and/or other materials provided with
the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
THE POSSIBILITY OF SUCH DAMAGE.
-->
<xsl:output indent="no" omit-xml-declaration="yes" method="text" encoding="UTF-8" media-type="text/x-json"/>
<xsl:strip-space elements="*"/>
<!--contant-->
<xsl:variable name="d">0123456789</xsl:variable>
<!-- ignore document text -->
<xsl:template match="text()[preceding-sibling::node() or following-sibling::node()]"/>
<!-- string -->
<xsl:template match="text()">
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="."/>
</xsl:call-template>
</xsl:template>
<!-- Main template for escaping strings; used by above template and for object-properties
Responsibilities: placed quotes around string, and chain up to next filter, escape-bs-string -->
<xsl:template name="escape-string">
<xsl:param name="s"/>
<xsl:text>"</xsl:text>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
<xsl:text>"</xsl:text>
</xsl:template>
<!-- Escape the backslash (\) before everything else. -->
<xsl:template name="escape-bs-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'\')">
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="concat(substring-before($s,'\'),'\\')"/>
</xsl:call-template>
<xsl:call-template name="escape-bs-string">
<xsl:with-param name="s" select="substring-after($s,'\')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Escape the double quote ("). -->
<xsl:template name="escape-quot-string">
<xsl:param name="s"/>
<xsl:choose>
<xsl:when test="contains($s,'&quot;')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'&quot;'),'\&quot;')"/>
</xsl:call-template>
<xsl:call-template name="escape-quot-string">
<xsl:with-param name="s" select="substring-after($s,'&quot;')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="$s"/>
</xsl:call-template>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- Replace tab, line feed and/or carriage return by its matching escape code. Can't escape backslash
or double quote here, because they don't replace characters (&#x0; becomes \t), but they prefix
characters (\ becomes \\). Besides, backslash should be seperate anyway, because it should be
processed first. This function can't do that. -->
<xsl:template name="encode-string">
<xsl:param name="s"/>
<xsl:choose>
<!-- tab -->
<xsl:when test="contains($s,'&#x9;')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'&#x9;'),'\t',substring-after($s,'&#x9;'))"/>
</xsl:call-template>
</xsl:when>
<!-- line feed -->
<xsl:when test="contains($s,'&#xA;')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'&#xA;'),'\n',substring-after($s,'&#xA;'))"/>
</xsl:call-template>
</xsl:when>
<!-- carriage return -->
<xsl:when test="contains($s,'&#xD;')">
<xsl:call-template name="encode-string">
<xsl:with-param name="s" select="concat(substring-before($s,'&#xD;'),'\r',substring-after($s,'&#xD;'))"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise><xsl:value-of select="$s"/></xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- number (no support for javascript mantissa) -->
<xsl:template match="text()[not(
(starts-with(., '0' ) and . != '0' and not(starts-with(., '0.' ))) or
(starts-with(.,'-0' ) and . != '-0' and not(starts-with(.,'-0.' ))) or
string(number(translate(., '{{nls_numeric_characters}}', '.,')))='NaN'
)]">
<xsl:variable name="num_string" select="translate(., '{{nls_numeric_characters}}', '.,')"/>
<xsl:choose>
<xsl:when test="starts-with($num_string, '.')">
<xsl:value-of select="concat('0', $num_string)"/>
</xsl:when>
<xsl:otherwise>
<xsl:value-of select="$num_string"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<!-- boolean, case-insensitive -->
<xsl:template match="text()[translate(.,'TRUE','true')='true']">true</xsl:template>
<xsl:template match="text()[translate(.,'FALSE','false')='false']">false</xsl:template>
<!-- object -->
<xsl:template match="*" name="base">
<xsl:if test="not(preceding-sibling::*)">{</xsl:if>
<xsl:call-template name="escape-string">
<xsl:with-param name="s" select="name()"/>
</xsl:call-template>
<xsl:text>:</xsl:text>
<!-- check type of node -->
<xsl:choose>
<!-- null nodes -->
<xsl:when test="count(child::node())=0">null</xsl:when>
<!-- other nodes -->
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<!-- end of type check -->
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">}</xsl:if>
</xsl:template>
<!-- array -->
<xsl:template match="*[count(../*[name(../*)=name(.)])=count(../*) and count(../*)&gt;1]">
<xsl:if test="not(preceding-sibling::*)">[</xsl:if>
<xsl:choose>
<xsl:when test="not(child::node())">
<xsl:text>null</xsl:text>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="child::node()"/>
</xsl:otherwise>
</xsl:choose>
<xsl:if test="following-sibling::*">,</xsl:if>
<xsl:if test="not(following-sibling::*)">]</xsl:if>
</xsl:template>
<!-- convert root element to an anonymous container -->
<xsl:template match="/">
<xsl:apply-templates select="node()"/>
</xsl:template>
</xsl:stylesheet>^';
select value
into nls_numeric_characters
from nls_session_parameters
where parameter = 'NLS_NUMERIC_CHARACTERS';
return replace(stylesheet, '{{nls_numeric_characters}}', nls_numeric_characters);
end get_xml_to_json_stylesheet;
function ref_cursor_to_json (p_ref_cursor in sys_refcursor,
p_max_rows in number := null,
p_skip_rows in number := null) return pljson_list
as
l_ctx dbms_xmlgen.ctxhandle;
l_num_rows pls_integer;
l_xml xmltype;
l_xsl xmltype := xmltype(get_xml_to_json_stylesheet);
l_returnvalue clob;
begin
/*
Purpose: generate JSON from REF Cursor
Remarks:
Who Date Description
------ ---------- -------------------------------------
MBR 30.01.2010 Created
JKR 01.05.2010 Edited to fit in PL/JSON
*/
l_ctx := dbms_xmlgen.newcontext (p_ref_cursor);
dbms_xmlgen.setnullhandling (l_ctx, dbms_xmlgen.empty_tag);
-- for pagination
if p_max_rows is not null then
dbms_xmlgen.setmaxrows (l_ctx, p_max_rows);
end if;
if p_skip_rows is not null then
dbms_xmlgen.setskiprows (l_ctx, p_skip_rows);
end if;
-- get the XML content
l_xml := dbms_xmlgen.getxmltype (l_ctx, dbms_xmlgen.none);
l_num_rows := dbms_xmlgen.getnumrowsprocessed (l_ctx);
dbms_xmlgen.closecontext (l_ctx);
close p_ref_cursor;
if(l_num_rows = 0) then
return pljson_list();
end if;
--dbms_output.put_line(l_xml.getstringval);
-- perform the XSL transformation
SELECT l_xml.transform(l_xsl).getclobval()
INTO l_returnvalue
FROM DUAL;
--dbms_output.put_line(l_returnvalue);
if(l_num_rows > 1) then
return pljson_list(pljson(l_returnvalue).get('ROWSET'));
end if;
declare ret pljson_list := pljson_list();
begin
ret.append(
pljson(
pljson(l_returnvalue).get('ROWSET')
).get('ROW')
);
return ret;
end;
exception
when scanner_exception then
dbms_output.put('Scanner problem with the following input: ');
dbms_output.put_line(l_returnvalue);
raise;
when parser_exception then
dbms_output.put('Parser problem with the following input: ');
dbms_output.put_line(l_returnvalue);
raise;
when others then raise;
end ref_cursor_to_json;
function sql_to_json (p_sql in varchar2,
p_max_rows in number := null,
p_skip_rows in number := null) return pljson_list
as
v_cur sys_refcursor;
begin
open v_cur for p_sql;
return ref_cursor_to_json(v_cur, p_max_rows, p_skip_rows);
end sql_to_json;
end pljson_util_pkg;```

162
docs/packages/PLJSON_XML.md Normal file
View File

@@ -0,0 +1,162 @@
# PLJSON_XML
## Package Specification
```sql
package pljson_xml as
/*
Copyright (c) 2010 Jonas Krogsboell
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/
/*
declare
obj json := json('{a:1, b:[2, 3, 4], c:true}');
x xmltype;
begin
obj.print;
x := json_xml.json_to_xml(obj);
dbms_output.put_line(x.getclobval());
end;
*/
function json_to_xml(obj pljson, tagname varchar2 default 'root') return xmltype;
end pljson_xml;```
## Package Body
```sql
package body pljson_xml as
function escapeStr(str varchar2) return varchar2 as
buf varchar2(32767) := '';
ch varchar2(4);
begin
for i in 1 .. length(str) loop
ch := substr(str, i, 1);
case ch
when '&' then buf := buf || '&amp;';
when '<' then buf := buf || '&lt;';
when '>' then buf := buf || '&gt;';
when '"' then buf := buf || '&quot;';
else buf := buf || ch;
end case;
end loop;
return buf;
end escapeStr;
/* Clob methods from printer */
procedure add_to_clob(buf_lob in out nocopy clob, buf_str in out nocopy varchar2, str varchar2) as
begin
-- if (length(str) > 5000 - length(buf_str)) then
if (lengthb(str) > 32767 - lengthb(buf_str)) then
dbms_lob.append(buf_lob, buf_str);
buf_str := str;
else
buf_str := buf_str || str;
end if;
end add_to_clob;
procedure flush_clob(buf_lob in out nocopy clob, buf_str in out nocopy varchar2) as
begin
dbms_lob.append(buf_lob, buf_str);
end flush_clob;
procedure toString(obj pljson_element, tagname in varchar2, xmlstr in out nocopy clob, xmlbuf in out nocopy varchar2) as
v_obj pljson;
v_list pljson_list;
v_keys pljson_list;
v_value pljson_element;
key_str varchar2(4000);
begin
if (obj.is_object()) then
add_to_clob(xmlstr, xmlbuf, '<' || tagname || '>');
v_obj := pljson(obj);
v_keys := v_obj.get_keys();
for i in 1 .. v_keys.count loop
v_value := v_obj.get(i);
key_str := v_keys.get(i).get_string();
if (key_str = 'content') then
if (v_value.is_array()) then
declare
v_l pljson_list := pljson_list(v_value);
begin
for j in 1 .. v_l.count loop
if (j > 1) then add_to_clob(xmlstr, xmlbuf, chr(13)||chr(10)); end if;
add_to_clob(xmlstr, xmlbuf, escapeStr(v_l.get(j).to_char()));
end loop;
end;
else
add_to_clob(xmlstr, xmlbuf, escapeStr(v_value.to_char()));
end if;
elsif (v_value.is_array()) then
declare
v_l pljson_list := pljson_list(v_value);
begin
for j in 1 .. v_l.count loop
v_value := v_l.get(j);
if (v_value.is_array()) then
add_to_clob(xmlstr, xmlbuf, '<' || key_str || '>');
add_to_clob(xmlstr, xmlbuf, escapeStr(v_value.to_char()));
add_to_clob(xmlstr, xmlbuf, '</' || key_str || '>');
else
toString(v_value, key_str, xmlstr, xmlbuf);
end if;
end loop;
end;
elsif (v_value.is_null() or (v_value.is_string() and v_value.get_string() = '')) then
add_to_clob(xmlstr, xmlbuf, '<' || key_str || '/>');
else
toString(v_value, key_str, xmlstr, xmlbuf);
end if;
end loop;
add_to_clob(xmlstr, xmlbuf, '</' || tagname || '>');
elsif (obj.is_array()) then
v_list := pljson_list(obj);
for i in 1 .. v_list.count loop
v_value := v_list.get(i);
toString(v_value, nvl(tagname, 'array'), xmlstr, xmlbuf);
end loop;
else
add_to_clob(xmlstr, xmlbuf, '<' || tagname || '>'||case when obj.value_of() is not null then escapeStr(obj.value_of()) end ||'</' || tagname || '>');
end if;
end toString;
function json_to_xml(obj pljson, tagname varchar2 default 'root') return xmltype as
xmlstr clob := empty_clob();
xmlbuf varchar2(32767) := '';
returnValue xmltype;
begin
dbms_lob.createtemporary(xmlstr, true);
toString(obj, tagname, xmlstr, xmlbuf);
flush_clob(xmlstr, xmlbuf);
returnValue := xmltype('<?xml version="1.0"?>'||xmlstr);
dbms_lob.freetemporary(xmlstr);
return returnValue;
end;
end pljson_xml;```

102
docs/packages/README.md Normal file
View File

@@ -0,0 +1,102 @@
# Packages Database
Questa cartella contiene la documentazione di tutti i 17 packages del database.
## Packages Business
| Package | Descrizione |
|---------|-------------|
| [MAIL_PKG](MAIL_PKG.md) | Gestione invio email automatiche (solleciti, reminder) |
## Packages Utility
| Package | Descrizione |
|---------|-------------|
| [UTL_BASE64](UTL_BASE64.md) | Encoding/decoding Base64 |
## Packages JasperReports (XLIB)
| Package | Descrizione |
|---------|-------------|
| [XLIB_JASPERREPORTS](XLIB_JASPERREPORTS.md) | Integrazione con JasperReports server |
| [XLIB_JASPERREPORTS_IMG](XLIB_JASPERREPORTS_IMG.md) | Gestione immagini nei report |
| [XLIB_HTTP](XLIB_HTTP.md) | Chiamate HTTP/REST |
| [XLIB_COMPONENT](XLIB_COMPONENT.md) | Gestione componenti |
| [XLIB_LOG](XLIB_LOG.md) | Sistema di logging |
## Packages JSON (PLJSON Library)
Libreria esterna per parsing e generazione JSON. In .NET può essere sostituita con `System.Text.Json` o `Newtonsoft.Json`.
| Package | Descrizione |
|---------|-------------|
| [PLJSON_DYN](PLJSON_DYN.md) | Query dinamiche JSON |
| [PLJSON_EXT](PLJSON_EXT.md) | Estensioni JSON |
| [PLJSON_HELPER](PLJSON_HELPER.md) | Helper functions |
| [PLJSON_ML](PLJSON_ML.md) | Multi-line JSON |
| [PLJSON_OBJECT_CACHE](PLJSON_OBJECT_CACHE.md) | Cache oggetti JSON |
| [PLJSON_PARSER](PLJSON_PARSER.md) | Parser JSON |
| [PLJSON_PRINTER](PLJSON_PRINTER.md) | Output JSON formattato |
| [PLJSON_UT](PLJSON_UT.md) | Unit test JSON |
| [PLJSON_UTIL_PKG](PLJSON_UTIL_PKG.md) | Utility JSON |
| [PLJSON_XML](PLJSON_XML.md) | Conversione JSON ↔ XML |
## Dettaglio Package MAIL_PKG
### Procedures Disponibili
| Procedura | Descrizione |
|-----------|-------------|
| `send_custom_mail` | Invio email generica |
| `send_richiesta_riscontro_preventivo` | Sollecito per preventivi in stato 100/200 |
| `send_richiesta_riscontro_preventivo_job` | Job: 10 giorni dopo DATA_DOC |
| `send_richiesta_riscontro_post_degustazione` | Sollecito post-degustazione |
| `send_richiesta_riscontro_post_degustazione_job` | Job: 15 giorni dopo prima degustazione |
| `send_reminder_seconda_caparra` | Reminder pagamento seconda caparra |
| `send_reminder_seconda_caparra_job` | Job: ogni 5 giorni da 65gg prima evento |
### Configurazione Email
- **From**: `noreply@apollinarecatering.it`
- **BCC**: `monia@apollinarecatering.it, matrimonio@apollinarecatering.it`
- Usa `APEX_MAIL` per invio
- Richiede `CMN_MAIL_HTMLUTILS` per costruzione body HTML
### Migrazione .NET
```csharp
// Esempio implementazione con SendGrid o SMTP
public interface IMailService
{
Task SendCustomMailAsync(string recipients, string subject, string body);
Task SendRichiestaRiscontroPreventivo(int eventoId);
Task SendReminderSecondaCaparra(int eventoId);
}
// Jobs con Hangfire
[RecurringJob("0 9 * * *")]
public async Task SendReminderSecondaCaparraJob()
{
var eventi = await GetEventiDaPagareEntro65gg();
foreach (var evento in eventi.Where(e => ShouldSendReminder(e)))
{
await _mailService.SendReminderSecondaCaparra(evento.Id);
}
}
```
## Note per Migrazione
1. **MAIL_PKG**: Sostituire con servizio email .NET (SendGrid, SMTP, Azure Communication Services)
2. **PLJSON_***: Non necessari in .NET, usare `System.Text.Json`
3. **XLIB_JASPERREPORTS**: Valutare alternative:
- SSRS (SQL Server Reporting Services)
- DevExpress Reports
- Telerik Reporting
- QuestPDF / iTextSharp per PDF
4. **XLIB_HTTP**: Sostituire con `HttpClient`
5. **UTL_BASE64**: Usare `Convert.ToBase64String` / `Convert.FromBase64String`

168
docs/packages/UTL_BASE64.md Normal file
View File

@@ -0,0 +1,168 @@
# UTL_BASE64
## Package Specification
```sql
PACKAGE "UTL_BASE64" is
function decode_base64(p_clob_in in clob) return blob;
function encode_base64(p_blob_in in blob) return clob;
FUNCTION encodeBlob2Base64(pBlobIn IN BLOB) RETURN BLOB;
FUNCTION decodeBase642Blob(pBlobIn IN BLOB) RETURN BLOB;
function base64encode(p_blob in blob) return clob;
end;
```
## Package Body
```sql
PACKAGE BODY "UTL_BASE64" is
function decode_base64(p_clob_in in clob) return blob is
v_blob blob;
v_result blob;
v_offset integer;
v_buffer_size binary_integer := 48;
v_buffer_varchar varchar2(48);
v_buffer_raw raw(48);
begin
if p_clob_in is null then
return null;
end if;
dbms_lob.createtemporary(v_blob, true);
v_offset := 1;
for i in 1 .. ceil(dbms_lob.getlength(p_clob_in) / v_buffer_size) loop
dbms_lob.read(p_clob_in, v_buffer_size, v_offset, v_buffer_varchar);
v_buffer_raw := utl_raw.cast_to_raw(v_buffer_varchar);
v_buffer_raw := utl_encode.base64_decode(v_buffer_raw);
dbms_lob.writeappend(v_blob, utl_raw.length(v_buffer_raw), v_buffer_raw);
v_offset := v_offset + v_buffer_size;
end loop;
v_result := v_blob;
dbms_lob.freetemporary(v_blob);
return v_result;
end decode_base64;
function encode_base64(p_blob_in in blob) return clob is
v_clob clob;
v_result clob;
v_offset integer;
v_chunk_size binary_integer := (48 / 4) * 3;
v_buffer_varchar varchar2(48);
v_buffer_raw raw(48);
begin
if p_blob_in is null then
return null;
end if;
dbms_lob.createtemporary(v_clob, true);
v_offset := 1;
for i in 1 .. ceil(dbms_lob.getlength(p_blob_in) / v_chunk_size) loop
dbms_lob.read(p_blob_in, v_chunk_size, v_offset, v_buffer_raw);
v_buffer_raw := utl_encode.base64_encode(v_buffer_raw);
v_buffer_varchar := utl_raw.cast_to_varchar2(v_buffer_raw);
dbms_lob.writeappend(v_clob, length(v_buffer_varchar), v_buffer_varchar);
v_offset := v_offset + v_chunk_size;
end loop;
v_result := v_clob;
dbms_lob.freetemporary(v_clob);
return v_result;
end encode_base64;
FUNCTION encodeBlob2Base64(pBlobIn IN BLOB) RETURN BLOB IS
vAmount NUMBER := 45;
vBlobEnc BLOB := empty_blob();
vBlobEncLen NUMBER := 0;
vBlobInLen NUMBER := 0;
vBuffer RAW(45);
vOffset NUMBER := 1;
BEGIN
-- dbms_output.put_line('Start base64 encoding.');
vBlobInLen := dbms_lob.getlength(pBlobIn);
-- dbms_output.put_line('<BlobInLength>' || vBlobInLen);
dbms_lob.createtemporary(vBlobEnc, TRUE);
LOOP
IF vOffset >= vBlobInLen THEN
EXIT;
END IF;
dbms_lob.read(pBlobIn, vAmount, vOffset, vBuffer);
BEGIN
dbms_lob.append(vBlobEnc, utl_encode.base64_encode(vBuffer));
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('<vAmount>' || vAmount || '<vOffset>' || vOffset || '<vBuffer>' || vBuffer);
dbms_output.put_line('ERROR IN append: ' || SQLERRM);
RAISE;
END;
vOffset := vOffset + vAmount;
END LOOP;
vBlobEncLen := dbms_lob.getlength(vBlobEnc);
-- dbms_output.put_line('<BlobEncLength>' || vBlobEncLen);
-- dbms_output.put_line('Finshed base64 encoding.');
RETURN vBlobEnc;
END encodeBlob2Base64;
FUNCTION decodeBase642Blob(pBlobIn IN BLOB) RETURN BLOB IS
vAmount NUMBER := 256;--32;
vBlobDec BLOB := empty_blob();
vBlobDecLen NUMBER := 0;
vBlobInLen NUMBER := 0;
vBuffer RAW(256);--32);
vOffset NUMBER := 1;
BEGIN
-- dbms_output.put_line('Start base64 decoding.');
vBlobInLen := dbms_lob.getlength(pBlobIn);
-- dbms_output.put_line('<BlobInLength>' || vBlobInLen);
dbms_lob.createtemporary(vBlobDec, TRUE);
LOOP
IF vOffset >= vBlobInLen THEN
EXIT;
END IF;
dbms_lob.read(pBlobIn, vAmount, vOffset, vBuffer);
BEGIN
dbms_lob.append(vBlobDec, utl_encode.base64_decode(vBuffer));
EXCEPTION
WHEN OTHERS THEN
dbms_output.put_line('<vAmount>' || vAmount || '<vOffset>' || vOffset || '<vBuffer>' || vBuffer);
dbms_output.put_line('ERROR IN append: ' || SQLERRM);
RAISE;
END;
vOffset := vOffset + vAmount;
END LOOP;
vBlobDecLen := dbms_lob.getlength(vBlobDec);
-- dbms_output.put_line('<BlobDecLength>' || vBlobDecLen);
-- dbms_output.put_line('Finshed base64 decoding.');
RETURN vBlobDec;
END decodeBase642Blob;
function base64encode(p_blob in blob)
return clob
is
CRLF constant varchar2(2) := chr(13)||chr(10);
l_clob clob;
l_amount integer := 23826;
l_offset integer := 1;
l_raw raw(32767);
l_buf varchar2(32767);
l_len integer := dbms_lob.getlength(p_blob);
begin
dbms_lob.createtemporary(l_clob, true, dbms_lob.call);
while l_offset <= l_len loop
dbms_lob.read(p_blob, l_amount, l_offset, l_raw);
l_offset := l_offset + l_amount;
l_buf := utl_raw.cast_to_varchar2(utl_encode.base64_encode(l_raw));
l_buf := replace(l_buf, CRLF);
dbms_lob.writeappend(l_clob, length(l_buf), l_buf);
end loop;
return l_clob;
end base64encode;
end;
```

View File

@@ -0,0 +1,265 @@
# XLIB_COMPONENT
## Package Specification
```sql
PACKAGE "XLIB_COMPONENT"
AS
/*=========================================================================
$Id: xlib_component.pks 57 2013-05-13 07:09:51Z dietmar.aust $
Purpose :
License : Copyright (c) 2010 Dietmar Aust (opal-consulting.de)
Licensed under a BSD style license (license.txt)
http://www.opal-consulting.de/pls/apex/f?p=20090928:14
$LastChangedDate: 2013-05-13 09:09:51 +0200 (Mon, 13 May 2013) $
$LastChangedBy: dietmar.aust $
Date Author Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
06.01.2010 D. Aust Initial creation
=========================================================================*/
-- how many digits does the version key have?
-- 3 => e.g. 1.0.0
-- 4 => e.g. 1.0.0.0
c_num_version_components CONSTANT NUMBER := 6;
PROCEDURE create_component (
p_name xlib_components.comp_name%TYPE,
p_version xlib_components.comp_version%TYPE,
p_version_label xlib_components.comp_version_label%TYPE DEFAULT NULL,
p_depends_on xlib_components.comp_depends_on%TYPE DEFAULT NULL
);
PROCEDURE set_component (
p_name xlib_components.comp_name%TYPE,
p_version xlib_components.comp_version%TYPE,
p_version_label xlib_components.comp_version_label%TYPE DEFAULT NULL,
p_depends_on xlib_components.comp_depends_on%TYPE DEFAULT NULL
);
PROCEDURE delete_component (p_name IN xlib_components.comp_name%TYPE);
/*****
utility functions
****/
FUNCTION get_version (p_name IN xlib_components.comp_name%TYPE)
RETURN xlib_components.comp_version%TYPE;
FUNCTION make_version_string (p_version IN VARCHAR2)
RETURN VARCHAR2;
PROCEDURE verify_required_component (
p_comp_name IN VARCHAR2,
p_comp_version_min IN VARCHAR2
);
END xlib_component;```
## Package Body
```sql
PACKAGE BODY "XLIB_COMPONENT"
AS
/*=========================================================================
FILE : $Id: xlib_component.pkb 57 2013-05-13 07:09:51Z dietmar.aust $
=========================================================================*/
TYPE vc2_arr_t IS TABLE OF VARCHAR2 (32767 CHAR)
INDEX BY BINARY_INTEGER;
PROCEDURE create_component (
p_name xlib_components.comp_name%TYPE,
p_version xlib_components.comp_version%TYPE,
p_version_label xlib_components.comp_version_label%TYPE DEFAULT NULL,
p_depends_on xlib_components.comp_depends_on%TYPE DEFAULT NULL
)
IS
BEGIN
INSERT INTO xlib_components
(comp_id, comp_name, comp_version, comp_version_label, comp_depends_on
)
VALUES (xlib_seq.NEXTVAL, p_name, p_version, p_version_label, p_depends_on
);
END;
PROCEDURE set_component (
p_name xlib_components.comp_name%TYPE,
p_version xlib_components.comp_version%TYPE,
p_version_label xlib_components.comp_version_label%TYPE DEFAULT NULL,
p_depends_on xlib_components.comp_depends_on%TYPE DEFAULT NULL
)
IS
BEGIN
INSERT INTO xlib_components
(comp_id, comp_name, comp_version, comp_version_label, comp_depends_on
)
VALUES (xlib_seq.NEXTVAL, p_name, p_version, p_version_label, p_depends_on
);
EXCEPTION
WHEN DUP_VAL_ON_INDEX
THEN
UPDATE xlib_components
SET comp_version = p_version,
comp_version_label = p_version_label,
comp_depends_on = p_depends_on
WHERE comp_name = p_name;
IF SQL%ROWCOUNT = 0
THEN
raise_application_error (-20006,
'component ' || p_name || ' not found'
);
END IF;
END;
PROCEDURE delete_component (p_name IN xlib_components.comp_name%TYPE)
IS
BEGIN
-- delete component
DELETE FROM xlib_components
WHERE comp_name = p_name;
IF SQL%ROWCOUNT = 0
THEN
raise_application_error (-20001,
'Component ' || p_name || ' not found'
);
END IF;
END;
FUNCTION split_string (p_str IN VARCHAR2, p_sep IN VARCHAR2 DEFAULT ',')
RETURN vc2_arr_t
AS
l_string VARCHAR2 (32767) := p_str || p_sep;
l_sep_index PLS_INTEGER;
l_index PLS_INTEGER := 1;
l_tab vc2_arr_t;
BEGIN
-- assertions
IF LENGTH (p_sep) != 1
THEN
raise_application_error
(-20004,
'wrong separator format, must be only one character'
);
END IF;
LOOP
l_sep_index := INSTR (l_string, p_sep, l_index);
EXIT WHEN l_sep_index = 0;
l_tab (l_tab.COUNT) :=
SUBSTR (l_string, l_index, l_sep_index - l_index);
l_index := l_sep_index + 1;
END LOOP;
RETURN l_tab;
END;
FUNCTION get_version (p_name IN xlib_components.comp_name%TYPE)
RETURN xlib_components.comp_version%TYPE
IS
l_version xlib_components.comp_version%TYPE;
BEGIN
SELECT comp_version
INTO l_version
FROM xlib_components
WHERE comp_name = p_name;
RETURN l_version;
END;
PROCEDURE assert_version_format (p_version IN VARCHAR2)
IS
l_tab vc2_arr_t;
l_num NUMBER;
BEGIN
-- '.' at the beginning or end of the version?
IF SUBSTR (p_version, 1, 1) = '.'
OR SUBSTR (p_version, LENGTH (p_version), 1) = '.'
OR INSTR (p_version, ' ') > 0
THEN
raise_application_error (-20002, 'wrong version format');
END IF;
l_tab := split_string (p_version, '.');
FOR i IN 0 .. l_tab.COUNT - 1
LOOP
l_num := TO_NUMBER (l_tab (i));
END LOOP;
EXCEPTION
WHEN OTHERS
THEN
IF SQLCODE = -6502 /* numeric or value error */
THEN
raise_application_error (-20005,
'wrong version format, no numbers.'
);
ELSE
RAISE;
END IF;
END;
FUNCTION make_version_string (p_version IN VARCHAR2)
RETURN VARCHAR2
IS
l_num_dots NUMBER;
l_tab vc2_arr_t;
l_str VARCHAR2 (32767 CHAR);
l_comp VARCHAR2 (50 CHAR);
BEGIN
-- assertions
assert_version_format (p_version => p_version);
l_tab := split_string (p_version, '.');
FOR i IN 1 .. c_num_version_components
LOOP
IF l_tab.EXISTS (i - 1)
THEN
l_comp :=
TO_CHAR (TO_NUMBER (NVL (l_tab (i - 1), '0')), 'FM0000');
ELSE
l_comp := '0000';
END IF;
IF l_str IS NULL
THEN
l_str := l_comp;
ELSE
l_str := l_str || '.' || l_comp;
END IF;
END LOOP;
RETURN l_str;
END;
PROCEDURE verify_required_component (
p_comp_name IN VARCHAR2,
p_comp_version_min IN VARCHAR2
)
IS
l_current_version VARCHAR2 (50);
BEGIN
l_current_version := xlib_component.get_version (p_name => p_comp_name);
IF make_version_string (l_current_version)
>= make_version_string (p_comp_version_min)
THEN
NULL; -- ok
ELSE
raise_application_error
(-20020,
'this upgrade requires '||p_comp_name||' in version '
|| p_comp_version_min
|| ' or higher, not version '
|| l_current_version
);
END IF;
END;
END xlib_component;```

477
docs/packages/XLIB_HTTP.md Normal file
View File

@@ -0,0 +1,477 @@
# XLIB_HTTP
## Package Specification
```sql
PACKAGE "XLIB_HTTP"
AS
/*=========================================================================
Purpose : Make http callouts
License : Copyright (c) 2010 Dietmar Aust (opal-consulting.de)
Licensed under a BSD style license (license.txt)
http://www.opal-consulting.de/pls/apex/f?p=20090928:14
Version Date Author Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19.02.2007 D. Aust initial creation
07.08.2008 D. Aust - added check_get_request
- display_url_raw: pass all request headers
to the client
05.08.2012 D. Aust suppress mime header TRANSFER-ENCODING,
causes lots of problems with XMLDB listener
and others choking.
2.3.0.0 19.05.2014 D. Aust - #294 - Fix chunked encoding problem in
xlib_http.get_report
- added version information to this package
2.3.0.0 09.05.2015 D. Aust pass JSESSIONID from backend J2EE server to client
for image rendering in html reports
2.6.1 28.09.2020 D. Aust - #40 - APEX 20.1 security bundle (PSE 30990551) rejects response header "Cache-Control: private"
2.6.2 13.10.2020 D. Aust - added function check_acl()
=========================================================================*/
c_success CONSTANT CHAR (1) := '1';
c_fail CONSTANT CHAR (1) := '0';
-- version of this package
version_c constant varchar2(20 char) := '2.6.2';
TYPE vc_arr_t IS TABLE OF VARCHAR2 (32767) INDEX BY BINARY_INTEGER;
g_empty_vc_arr vc_arr_t;
/* Function: MyFunction
*
* Parameters:
*
* x - Description of x.
* y - Description of y.
* z - Description of z.
*/
PROCEDURE display_url_raw (
p_url VARCHAR2,
p_mime_type_override IN VARCHAR2 DEFAULT NULL,
p_charset IN VARCHAR2 DEFAULT NULL,
p_header_name_arr IN vc_arr_t default g_empty_vc_arr,
p_header_value_arr IN vc_arr_t default g_empty_vc_arr
);
/* Procedure: retrieve_blob_from_url
Multiplies two integers.
Parameters:
p_url - url to be called
o_blob - output: the resulting out blob
o_mime_type - output: the resulting out mime type from the call
Returns:
The two integers multiplied together.
o_blob - the resulting out blob
See Also:
<escape_form_data>
*/
PROCEDURE retrieve_blob_from_url (
p_url VARCHAR2,
o_blob OUT BLOB,
o_mime_type OUT VARCHAR2
);
/*
Function: escape_form_data
Here is some describing text ...
--- SQL
declare
l_i number;
begin
null;
Select count(*)
into l_count
from dual;
end;
---
Parameters:
s - string to be escaped
Returns:
the escaped data
*/
FUNCTION escape_form_data (s VARCHAR2)
RETURN VARCHAR2;
/*
Function: check_get_request
Parameters:
p_url the url to be called
Returns:
Returns c_fail or c_success
*/
FUNCTION check_get_request (p_url VARCHAR2)
RETURN CHAR;
/*
Function: check_acl
Parameters:
p_url the url to be called
Returns:
Returns c_fail or c_success
*/
FUNCTION check_acl (p_url VARCHAR2)
RETURN CHAR;
END;```
## Package Body
```sql
PACKAGE BODY "XLIB_HTTP"
AS
/*=========================================================================
Purpose : Make http callouts
License : Copyright (c) 2010 Dietmar Aust (opal-consulting.de)
Licensed under a BSD style license (license.txt)
https://github.com/daust/JasperReportsIntegration
Version Date Author Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
19.02.2007 D. Aust initial creation
07.08.2008 D. Aust - added check_get_request
- display_url_raw: pass all request headers
to the client
05.08.2012 D. Aust suppress mime header TRANSFER-ENCODING,
causes lots of problems with XMLDB listener
and others choking.
2.3.0.0 19.05.2014 D. Aust - #294 - Fix chunked encoding problem in
xlib_http.get_report
- added version information to this package
2.3.0.0 09.05.2015 D. Aust pass JSESSIONID from backend J2EE server to client
for image rendering in html reports
2.6.1 28.09.2020 D. Aust - #40 - APEX 20.1 security bundle (PSE 30990551) rejects response header "Cache-Control: private"
2.6.2 13.10.2020 D. Aust - added function check_acl()
=========================================================================*/
m_module VARCHAR2 (50) := 'XLIB_HTTP';
PROCEDURE display_url_raw (
p_url VARCHAR2,
p_mime_type_override IN VARCHAR2 DEFAULT NULL,
p_charset IN VARCHAR2 DEFAULT NULL,
p_header_name_arr IN vc_arr_t default g_empty_vc_arr,
p_header_value_arr IN vc_arr_t default g_empty_vc_arr
)
IS
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_blob BLOB;
l_raw RAW (32767);
l_buffer_size NUMBER := 32767;
--
l_proc VARCHAR2 (100) := m_module || '.DISPLAY_URL_RAW';
--
l_mime_type VARCHAR2 (100);
l_header_name VARCHAR2 (256);
l_header_value VARCHAR2 (1024);
l_jsession VARCHAR2 (256);
l_path VARCHAR2 (1024);
--
l_header_name_arr vc_arr_t;
l_header_value_arr vc_arr_t;
--
l_msg varchar2(32767);
BEGIN
xlog (l_proc, 'show url: ' || p_url);
--htp.flush();
--htp.init();
-- Initialize the BLOB.
DBMS_LOB.createtemporary (l_blob, FALSE);
l_http_request := UTL_HTTP.begin_request (url => p_url,
method => 'GET',
http_version => utl_http.http_version_1_0);
utl_http.set_header (l_http_request, 'Connection', 'Keep-Alive');
-- pass additional headers to the target service
for i in 1..p_header_name_arr.count loop
xlog(l_proc, 'pass additional headers to target service: '|| p_header_name_arr(i) ||': '||p_header_value_arr(i));
utl_http.set_header(l_http_request, p_header_name_arr(i), p_header_value_arr(i));
end loop;
-- get response from target service
l_http_response := UTL_HTTP.get_response (l_http_request);
FOR i IN 1 .. UTL_HTTP.get_header_count (l_http_response)
LOOP
UTL_HTTP.get_header (l_http_response,
i,
l_header_name,
l_header_value
);
-- store header value in arr
l_header_name_arr (i) := l_header_name;
l_header_value_arr (i) := l_header_value;
IF LOWER (l_header_name) = 'content-type'
THEN
l_mime_type := l_header_value;
xlog(l_proc, 'content-type from server: ' || l_mime_type);
END IF;
END LOOP;
-- override mime type
IF p_mime_type_override IS NOT NULL
THEN
l_mime_type := p_mime_type_override;
END IF;
-- Copy the response into the BLOB.
BEGIN
LOOP
UTL_HTTP.read_raw (l_http_response, l_raw, l_buffer_size);
DBMS_LOB.writeappend (l_blob, UTL_RAW.LENGTH (l_raw), l_raw);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response (l_http_response);
END;
xlog(l_proc, 'set content-type: ' || l_mime_type);
OWA_UTIL.mime_header (ccontent_type => l_mime_type,
bclose_header => FALSE,
ccharset => p_charset
);
FOR i IN 1 .. l_header_name_arr.COUNT
LOOP
IF UPPER (l_header_name_arr (i)) IN
('CONTENT-LENGTH', 'CONTENT-TYPE', 'MIME-TYPE', 'TRANSFER-ENCODING',
'STRICT-TRANSPORT-SECURITY', 'CACHE-CONTROL', 'PRAGMA', 'EXPIRES')
THEN
--xlog (l_proc, 'skip header ' || l_header_name_arr (i));
null;
ELSE
if upper(l_header_name_arr(i)) = 'SET-COOKIE' and l_header_value_arr (i) like 'JSESSIONID%' then
xlog(l_proc , 'JSESSION_ID found !!!:'||l_header_value_arr (i));
--extract path
l_jsession := regexp_substr(l_header_value_arr (i), 'JSESSIONID=(.*);[ ]*Path',1, 1,'i',1);
l_path := regexp_substr(l_header_value_arr (i), ';[ ]*Path=(.*)',1, 1,'i',1);
xlog(l_proc, 'xx:full:'||l_header_value_arr (i)|| '; xx:session:'||l_jsession || '; xx:path:'||l_path);
else
l_header_value := l_header_value_arr (i);
end if;
xlog (l_proc,
'set header:'
|| l_header_name_arr (i)
|| ': '
|| l_header_value
);
HTP.p (l_header_name_arr (i) || ': ' || l_header_value);
END IF;
END LOOP;
-- JSESSION Cookies ausgeben
-- if using tunnel, then the cookie is JRI_JSESSIONID
-- if not using tunnel, then cookie is JSESSIONID directly
--
if xlib_jasperreports.get_use_images_no_tunnel=false then
l_msg := 'Set-Cookie: ' || xlib_jasperreports.m_jri_cookie_name_c || '=' || l_jsession;
xlog (l_proc, 'set header:' || l_msg );
HTP.p (l_msg);
l_msg := 'Set-Cookie: ' || xlib_jasperreports.m_jri_path_cookie_name_c || '=' || l_path;
xlog (l_proc, 'set header:' || l_msg );
HTP.p (l_msg);
else
l_msg := 'Set-Cookie: JSESSIONID=' || l_jsession;
if xlib_jasperreports.get_cookie_path_no_tunnel is not null then
l_msg := l_msg || '; Path=' || xlib_jasperreports.get_cookie_path_no_tunnel;
end if;
xlog (l_proc, 'set header:' || l_msg );
HTP.p (l_msg);
end if;
-- set content length
HTP.p ('Content-length: ' || DBMS_LOB.getlength (l_blob));
OWA_UTIL.http_header_close;
WPG_DOCLOAD.download_file (l_blob);
-- Relase the resources associated with the temporary LOB.
DBMS_LOB.freetemporary (l_blob);
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response (l_http_response);
DBMS_LOB.freetemporary (l_blob);
RAISE;
WHEN OTHERS
THEN
xlog (l_proc, 'Error: ' || SQLERRM, 'ERROR');
RAISE;
END;
PROCEDURE retrieve_blob_from_url (
p_url VARCHAR2,
o_blob OUT BLOB,
o_mime_type OUT VARCHAR2
)
IS
l_http_request UTL_HTTP.req;
l_http_response UTL_HTTP.resp;
l_raw RAW (32767);
--
l_proc VARCHAR2 (100)
:= m_module || '.RETRIEVE_BLOB_FROM_URL';
--
l_header_name VARCHAR2 (256);
l_header_value VARCHAR2 (1024);
BEGIN
-- Initialize the BLOB.
dbms_lob.createtemporary (o_blob, false);
l_http_request := utl_http.begin_request (url => p_url,
method => 'GET',
http_version => utl_http.http_version_1_0);
l_http_response := UTL_HTTP.get_response (l_http_request);
FOR i IN 1 .. UTL_HTTP.get_header_count (l_http_response)
LOOP
UTL_HTTP.get_header (l_http_response,
i,
l_header_name,
l_header_value
);
IF LOWER (l_header_name) = 'content-type'
THEN
o_mime_type := l_header_value;
END IF;
END LOOP;
-- Copy the response into the BLOB.
BEGIN
LOOP
UTL_HTTP.read_raw (l_http_response, l_raw, 32767);
DBMS_LOB.writeappend (o_blob, UTL_RAW.LENGTH (l_raw), l_raw);
END LOOP;
EXCEPTION
WHEN UTL_HTTP.end_of_body
THEN
UTL_HTTP.end_response (l_http_response);
END;
-- Relase the resources associated with the temporary LOB.
--DBMS_LOB.freetemporary (l_blob);
EXCEPTION
WHEN OTHERS
THEN
UTL_HTTP.end_response (l_http_response);
DBMS_LOB.freetemporary (o_blob);
xlog (l_proc, 'Error: ' || SQLERRM, 'ERROR');
RAISE;
END;
FUNCTION escape_form_data (s VARCHAR2)
RETURN VARCHAR2
IS
l_s VARCHAR2 (500 CHAR);
FUNCTION r (s VARCHAR2, c VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
RETURN REPLACE (s, c, '%' || TRIM (TO_CHAR (ASCII (c), 'XX')));
END;
BEGIN
l_s := s;
l_s := REPLACE (l_s, ' ', '+');
l_s := r (l_s, chr(37)); -- %
l_s := r (l_s, chr(37)); -- /
l_s := r (l_s, chr(63)); -- ?
l_s := r (l_s, chr(38)); -- &
l_s := r (l_s, chr(228)); -- <20><><EFBFBD>
l_s := r (l_s, chr(196)); -- <20><>
l_s := r (l_s, chr(246)); -- <20><>
l_s := r (l_s, chr(214)); -- <20><>
l_s := r (l_s, chr(252)); -- <20><>
l_s := r (l_s, chr(220)); -- <20><><EFBFBD>
l_s := r (l_s, chr(223)); -- <20><><EFBFBD>
RETURN l_s;
END;
FUNCTION check_get_request (p_url VARCHAR2)
RETURN CHAR
IS
l_clob CLOB;
BEGIN
IF p_url IS NULL
THEN
RETURN c_fail;
END IF;
SELECT HTTPURITYPE (p_url).getclob ()
INTO l_clob
FROM DUAL;
/*SELECT c_success
INTO l_ret
FROM DUAL
WHERE EXISTS (SELECT HTTPURITYPE (p_url).getclob ()
FROM DUAL);
*/
RETURN c_success;
EXCEPTION
WHEN OTHERS
THEN
RETURN c_fail;
END;
FUNCTION check_acl (p_url VARCHAR2)
RETURN CHAR
IS
l_clob CLOB;
BEGIN
IF p_url IS NULL
THEN
RETURN c_fail;
END IF;
SELECT HTTPURITYPE (p_url).getclob ()
INTO l_clob
FROM DUAL;
/*SELECT c_success
INTO l_ret
FROM DUAL
WHERE EXISTS (SELECT HTTPURITYPE (p_url).getclob ()
FROM DUAL);
*/
RETURN c_success;
EXCEPTION
WHEN OTHERS
THEN
-- acl problem
if sqlcode=24247 then
RETURN c_fail;
else
-- no acl problem
return c_success;
end if;
END;
END;```

View File

@@ -0,0 +1,721 @@
# XLIB_JASPERREPORTS
## Package Specification
```sql
PACKAGE "XLIB_JASPERREPORTS"
AS
/*=========================================================================
Purpose :
License : Copyright (c) 2010 Dietmar Aust (opal-consulting.de)
Licensed under a BSD style license (license.txt)
http://www.opal-consulting.de/pls/apex/f?p=20090928:14
$LastChangedDate: 2018-09-30 09:00:44 +0200 (So, 30 Sep 2018) $
$LastChangedBy: dietmar.aust $
Version Date Author Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
06.01.2010 D. Aust Initial creation
06.05.2011 D. Aust added constants for xlsx and docx
05.08.2012 D. Aust added version 2.0.0 features:
- direct printing
- save file on server
11.05.2013 D. Aust added support for tunneling images for html
exports only
2.3.0.0 19.05.2014 D. Aust - #294 - Fix chunked encoding problem in
xlib_http.get_report
- added version information to this package
2.4.0.0 15.10.2017 D. Aust FEATURE: #3941 - Support for timeZones
(report parameter REPORT_TIME_ZONE)
2.5.0.0 29.09.2018 D. Aust FEATURE: #9 - Ability to set Printjob name (programmatically)
2.5.0.1 30.09.2018 D. Aust fix bool2string issue
2.6.1 01.10.2020 D. Aust add get_default_configuration() and set_default_configuration()
2.6.2 13.10.2020 D. Aust #54 - Timeout value from default table not working
=========================================================================*/
-- version of this package
version_c constant varchar2(20 char) := '2.6.2';
-- constants
-- supported formats
c_rep_format_pdf CONSTANT VARCHAR2 (20) := 'pdf';
c_rep_format_rtf CONSTANT VARCHAR2 (20) := 'rtf';
c_rep_format_xls CONSTANT VARCHAR2 (20) := 'xls';
c_rep_format_html constant varchar2 (20) := 'html';
c_rep_format_html2 CONSTANT VARCHAR2 (20) := 'html2';
c_rep_format_csv CONSTANT VARCHAR2 (20) := 'csv';
c_rep_format_docx CONSTANT VARCHAR2 (20) := 'docx';
c_rep_format_pptx CONSTANT VARCHAR2 (20) := 'pptx';
c_rep_format_xlsx CONSTANT VARCHAR2 (20) := 'xlsx';
-- images uri
--c_images_uri_tunnel constant varchar2(100 char)
-- := 'xlib_jasperreports_img.get_image?p_url=#REPORT_URL#&p_image=';
-- #IMAGE_NAME# and #J2EE_CONTEXT# will be substituted on the J2EE server side
m_jri_cookie_name_c CONSTANT VARCHAR2 (50) := 'JRI_SESSIONID';
m_jri_path_cookie_name_c CONSTANT VARCHAR2 (50) := 'JRI_PATH';
c_images_uri_tunnel constant varchar2(500 char) := 'wwv_flow.show?p_request=APPLICATION_PROCESS%3DJRI_SHOW_IMAGE&p_flow_id=#APP_ID#&p_flow_step_id=0&p_instance=#APP_SESSION#&x01=#IMAGE_NAME#';
c_images_uri_no_tunnel constant varchar2(500 char) := '#J2EE_CONTEXT_PATH#/report_image?image=#IMAGE_NAME#';
-- exceptions
report_url_not_defined EXCEPTION;
----------------------------------------------------------------------------
-- sets the url for the report server for all requests in the
-- current session
----------------------------------------------------------------------------
PROCEDURE set_report_url (p_report_url IN VARCHAR2);
FUNCTION get_report_url
RETURN VARCHAR2;
----------------------------------------------------------------------------
-- set the image uri for html reports only!
----------------------------------------------------------------------------
PROCEDURE set_images_uri (p_images_uri IN VARCHAR2 default null);
procedure use_images_no_tunnel (p_server_uri in varchar2 default null, p_cookie_path varchar2 default null);
function get_use_images_no_tunnel return boolean;
FUNCTION get_images_uri
return varchar2;
FUNCTION get_cookie_path_no_tunnel
RETURN VARCHAR2;
/** make a callout with utl_http to the j2ee container running the
* JasperReportsIntegration web application
* => return the results
*
* @param p_rep_name name of the report (needs a name.jasper file deployed on the server)
* @param p_rep_format report format, e.g. pdf, rtf, etc, see constants
* @param p_data_source data source name, needs to be configured in J2EE application
* @param p_out_filename filename if the file should be downloaded
* @param p_rep_locale report locale setting, e.g. de_DE or en_US
* @param p_rep_encoding encoding, e.g. UTF-8
* @param p_additional_params additional parameters, e.g.: p1=1&p2=2
* @param p_print_is_enabled shall the report be sent to the printer directly?
* @param p_print_printer_name name or substring of printer name
* @param p_print_media media used, either the paper size or the tray
* @param p_print_copies number of copies to be printed
* @param p_print_duplex duplex printing?
* @param p_print_collate sorting the print output?
* @param p_save_is_enabled shall the generated file be saved on the server?
* @param p_rep_time_zone "time zone" parameter for the execution of the report,
a list of valid entries can be found here:
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
E.g.: Europe/Berlin, UCT, US/Central, US/Pacific,
Etc/Greenwich, Europe/London
* @param p_save_filename filename for the file to be saved on the server
* @param p_print_job_name name of the print job name, by default it uses: JasperReports - <report name>
*
*/
PROCEDURE show_report (
p_rep_name IN VARCHAR2 DEFAULT 'test',
p_rep_format IN VARCHAR2 DEFAULT c_rep_format_pdf,
p_data_source IN VARCHAR2 DEFAULT 'default',
p_out_filename IN VARCHAR2 DEFAULT NULL,
p_rep_locale in varchar2 default 'de_DE',
p_rep_encoding in varchar2 default 'UTF-8',
p_additional_params in varchar2 default null,
p_print_is_enabled in boolean default false,
p_print_printer_name in varchar2 default null,
p_print_media in varchar2 default null,
p_print_copies in number default 1,
p_print_duplex in boolean default false,
p_print_collate in boolean default false,
p_save_is_enabled in boolean default false,
p_save_filename in varchar2 default null,
p_rep_time_zone in varchar2 default null,
p_print_job_name in varchar2 default null
);
/* tunnels images for html reports */
procedure show_image(p_image_name IN VARCHAR2);
/** run the report and return the result as a blob
*
* @param p_rep_name name of the report (needs a name.jasper file deployed on the server)
* @param p_rep_format report format, e.g. pdf, rtf, etc, see constants
* @param p_data_source data source name, needs to be configured in J2EE application
* @param p_out_filename filename if the file should be downloaded
* @param p_rep_locale report locale setting, e.g. de_DE or en_US
* @param p_rep_encoding encoding, e.g. UTF-8
* @param p_additional_params additional parameters, e.g.: p1=1&p2=2
* @param p_print_is_enabled shall the report be sent to the printer directly?
* @param p_print_printer_name name or substring of printer name
* @param p_print_media media used, either the paper size or the tray
* @param p_print_copies number of copies to be printed
* @param p_print_duplex duplex printing?
* @param p_print_collate sorting the print output?
* @param p_save_is_enabled shall the generated file be saved on the server?
* @param p_save_filename filename for the file to be saved on the server
* @param p_rep_time_zone "time zone" parameter for the execution of the report,
a list of valid entries can be found here:
https://en.wikipedia.org/wiki/List_of_tz_database_time_zones
E.g.: Europe/Berlin, UCT, US/Central, US/Pacific,
Etc/Greenwich, Europe/London
* @param p_out_blob the blob will be returned here
* @param p_out_mime_type the proper mime type of the generated file
* @param p_print_job_name name of the print job name, by default it uses: JasperReports - <report name>
*
*/
PROCEDURE get_report (
p_rep_name IN VARCHAR2 DEFAULT 'test',
p_rep_format in varchar2 default c_rep_format_pdf,
p_data_source IN VARCHAR2 DEFAULT 'default',
p_rep_locale in varchar2 default 'de_DE',
p_rep_encoding IN VARCHAR2 DEFAULT 'UTF-8',
p_additional_params in varchar2 default null,
p_print_is_enabled in boolean default false,
p_print_printer_name in varchar2 default null,
p_print_media in varchar2 default null,
p_print_copies in number default 1,
p_print_duplex in boolean default false,
p_print_collate in boolean default false,
p_save_is_enabled in boolean default false,
p_save_filename in varchar2 default null,
p_rep_time_zone in varchar2 default null,
p_out_blob IN OUT BLOB,
p_out_mime_type IN OUT VARCHAR2,
p_print_job_name in varchar2 default null
);
----------------------------------------------------------------------------
-- get default configuration
----------------------------------------------------------------------------
FUNCTION get_default_configuration
return xlib_jasperreports_conf%rowtype;
----------------------------------------------------------------------------
-- set default configuration
----------------------------------------------------------------------------
PROCEDURE set_default_configuration(p_conf in out xlib_jasperreports_conf%rowtype);
PROCEDURE set_default_configuration (
p_protocol IN xlib_jasperreports_conf.conf_protocol%TYPE default 'http',
p_server IN xlib_jasperreports_conf.conf_server%TYPE default 'localhost',
p_port IN xlib_jasperreports_conf.conf_port%TYPE default '80',
p_context_path IN xlib_jasperreports_conf.conf_context_path%TYPE default 'jri',
p_wallet_path IN xlib_jasperreports_conf.conf_wallet_path%TYPE default null,
p_wallet_pwd IN xlib_jasperreports_conf.conf_wallet_pwd%TYPE default null,
p_http_transfer_timeout IN xlib_jasperreports_conf.conf_http_transfer_timeout%TYPE default 60);
END;```
## Package Body
```sql
PACKAGE BODY "XLIB_JASPERREPORTS"
AS
/*=========================================================================
Purpose :
License : Copyright (c) 2010 Dietmar Aust (opal-consulting.de)
Licensed under a BSD style license (license.txt)
http://www.opal-consulting.de/pls/apex/f?p=20090928:14
$LastChangedDate: 2018-09-30 09:00:44 +0200 (So, 30 Sep 2018) $
$LastChangedBy: dietmar.aust $
Version Date Author Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
06.01.2010 D. Aust Initial creation
06.05.2011 D. Aust added constants for xlsx and docx
05.08.2012 D. Aust added version 2.0.0 features:
- direct printing
- save file on server
11.05.2013 D. Aust added support for tunneling images for html
exports only
2.3.0.0 19.05.2014 D. Aust - #294 - Fix chunked encoding problem in
xlib_http.get_report
- added version information to this package
2.4.0.0 15.10.2017 D. Aust FEATURE: #3941 - Support for timeZones
(report parameter REPORT_TIME_ZONE)
2.5.0.0 29.09.2018 D. Aust FEATURE: #9 - Ability to set Printjob name (programmatically)
2.5.0.1 30.09.2018 D. Aust fix bool2string issue
2.6.1 01.10.2020 D. Aust add get_default_configuration() and set_default_configuration()
2.6.2 13.10.2020 D. Aust #54 - Timeout value from default table not working
=========================================================================*/
m_module constant varchar2 (100) := $$plsql_unit;
m_report_url VARCHAR2 (32767) := NULL;
m_images_uri VARCHAR2 (32767) := NULL;
m_use_images_no_tunnel boolean := false;
m_cookie_path_no_tunnel varchar2(200 char) := null;
-- convert boolean to 'true' or 'false'
FUNCTION bool2string (b IN BOOLEAN)
RETURN VARCHAR2
IS
BEGIN
IF b
THEN
RETURN 'true';
ELSE
RETURN 'false';
END IF;
END;
procedure setup_configuration_defaults
is
l_conf xlib_jasperreports_conf%rowtype;
l_timeout PLS_INTEGER;
begin
l_conf := get_default_configuration();
-- override report url if not defined
if m_report_url is null then
m_report_url := l_conf.conf_protocol || '://'||l_conf.conf_server||':'||l_conf.conf_port||'/'||l_conf.conf_context_path||'/report';
xlog(p_module => m_module, p_msg => 'Override report url from defaults: '|| m_report_url, p_type=> 'DEBUG');
end if;
-- set wallet path and pwd
-- will ALWAYS override the settings, will ignore previous calls to set_wallet
-- at least we check whether the config table has an entry for the wallet or not.
-- thus it could work relying on the wallet set by APEX through the internal workspace! :)
if lower(m_report_url) like 'https%' and l_conf.conf_wallet_path is not null then
utl_http.set_wallet(l_conf.conf_wallet_path, l_conf.conf_wallet_pwd);
xlog(p_module => m_module, p_msg => 'Override wallet location/pwd from defaults', p_type=> 'DEBUG');
end if;
-- override http transfer timeout it not defined
utl_http.get_transfer_timeout( timeout => l_timeout );
--xlog(p_module => m_module, p_msg => 'Current setting of transfer_timeout: '||l_timeout, p_type=> 'DEBUG');
if (l_timeout is null or l_timeout=60 /*60 is the default*/) then
utl_http.set_transfer_timeout(l_conf.conf_http_transfer_timeout);
xlog(p_module => m_module, p_msg => 'Override http transfer timeout ('||l_timeout||'s) from defaults: '|| l_conf.conf_http_transfer_timeout ||'s', p_type=> 'DEBUG');
end if;
end;
PROCEDURE dump_all_cookies
IS
l_proc VARCHAR2 (100) := m_module || '.dump_all_cookies';
l_name_arr OWA_COOKIE.vc_arr;
l_vals_arr OWA_COOKIE.vc_arr;
l_vals_ret INTEGER;
BEGIN
xlog (l_proc, 'start');
OWA_COOKIE.get_all (names => l_name_arr, vals => l_vals_arr, num_vals => l_vals_ret);
xlog (l_proc, '#num of cookies: ' || l_vals_ret);
FOR i IN 1 .. l_name_arr.COUNT
LOOP
xlog (l_proc, i || ':' || l_name_arr (i) || '=' || l_vals_arr (i));
END LOOP;
xlog (l_proc, 'end');
END;
----------------------------------------------------------------------------
-- sets the url for the report server for all requests in the
-- current session
----------------------------------------------------------------------------
PROCEDURE set_report_url (p_report_url IN VARCHAR2)
IS
BEGIN
xlog ('set_report_url:', p_report_url);
m_report_url := p_report_url;
END;
FUNCTION get_report_url
RETURN VARCHAR2
IS
BEGIN
RETURN m_report_url;
END;
----------------------------------------------------------------------------
-- sets the images_uri only for http reports!
----------------------------------------------------------------------------
PROCEDURE set_images_uri (p_images_uri IN VARCHAR2)
IS
BEGIN
m_images_uri := p_images_uri;
END;
/* use the images from the application server when both /ords and /jri are
installed on the same application server
the p_server_uri parameter is rarely used, only when they are not run on the same application server
and the uri differs. But that comes with a lot of CORS and other cookie issues.
*/
procedure use_images_no_tunnel (p_server_uri in varchar2 default null, p_cookie_path varchar2 default null)
is
begin
m_use_images_no_tunnel := true;
m_cookie_path_no_tunnel := p_cookie_path;
-- the placeholders #J2EE_CONTEXT_PATH# and #IMAGE_NAME# will be replaced
-- inside the J2EE application with the current values of the deployment
set_images_uri( p_images_uri => p_server_uri || '#J2EE_CONTEXT_PATH#/report_image?image=#IMAGE_NAME#');
end;
function get_use_images_no_tunnel return boolean
is
begin
return m_use_images_no_tunnel;
end;
FUNCTION get_cookie_path_no_tunnel
RETURN VARCHAR2
IS
BEGIN
RETURN m_cookie_path_no_tunnel;
END;
FUNCTION get_images_uri
RETURN VARCHAR2
IS
BEGIN
RETURN m_images_uri;
END;
FUNCTION compute_images_uri_tunnel
RETURN VARCHAR2
IS
l_uri VARCHAR2 (32767 CHAR);
BEGIN
--l_uri := sys_context( 'userenv', 'current_schema' )
-- || '.' || c_images_uri_tunnel;
/*
c_images_uri_tunnel
:='xlib_jasperreports_img.get_image?p_url=#REPORT_URL#&p_image=';
*/
--l_uri := replace(l_uri, '#REPORT_URL#', APEX_UTIL.URL_ENCODE(m_report_url));
--l_uri := replace(l_uri, '#REPORT_URL#', m_report_url);
-- c_images_uri_tunnel constant varchar2(100 char) := 'wwv_flow.show?p_request=APPLICATION_PROCESS%3DJRI_SHOW_IMAGE&p_flow_id=#APP_ID#&p_flow_step_id=0&p_instance=#APP_SESSION#&x01=#IMG_NAME#';
l_uri := c_images_uri_tunnel;
l_uri := REPLACE (l_uri, '#APP_ID#', v ('APP_ID'));
l_uri := REPLACE (l_uri, '#APP_SESSION#', v ('APP_SESSION'));
xlog ('compute tunnel', l_uri);
RETURN l_uri;
END;
----------------------------------------------------------------------------
-- displays an image for html reports
----------------------------------------------------------------------------
PROCEDURE show_image (p_image_name IN VARCHAR2)
IS
l_proc VARCHAR2 (100) := m_module || '.show_image';
l_url VARCHAR2 (32767);
l_header_name_arr xlib_http.vc_arr_t;
l_header_value_arr xlib_http.vc_arr_t;
PROCEDURE get_headers_to_pass2j2ee (p_header_name_arr OUT xlib_http.vc_arr_t,
p_header_value_arr OUT xlib_http.vc_arr_t)
IS
l_proc VARCHAR2 (100) := m_module || '.get_headers_to_pass2j2ee';
jsession_cookie OWA_COOKIE.cookie;
jsession_path_cookie OWA_COOKIE.cookie;
BEGIN
jsession_cookie := OWA_COOKIE.get (name => m_jri_cookie_name_c);
jsession_path_cookie := OWA_COOKIE.get (name => m_jri_path_cookie_name_c);
xlog (l_proc, 'show jsession_id and path: ');
xlog (l_proc, jsession_cookie.vals (1));
xlog (l_proc, jsession_path_cookie.vals (1));
p_header_name_arr (p_header_name_arr.COUNT + 1) := 'Cookie';
p_header_value_arr (p_header_value_arr.COUNT + 1) :=
'JSESSIONID=' || jsession_cookie.vals (1) || ';Path=' || jsession_path_cookie.vals (1);
--xlog(l_proc, 'end');
EXCEPTION
WHEN OTHERS
THEN
xlog (l_proc, DBMS_UTILITY.format_error_backtrace, 'ERROR');
END;
BEGIN
xlog (l_proc, 'start: ### SHOW IMAGE: ' || p_image_name);
dump_all_cookies;
-- pick up defaults from table xlib_jasperreports_conf
setup_configuration_defaults();
-------------------------------------------------------
-- assert valid values for the input variables
-------------------------------------------------------
IF m_report_url IS NULL
THEN
RAISE report_url_not_defined;
END IF;
-------------------------------------------------------
-- construct URL
-------------------------------------------------------
-- _image?image=img_0_0_15&uuid=b41eb881-7ca5-4919-bd8f-5afa8d10b398
l_url := m_report_url || '_image';
l_url := l_url || '?image=' || p_image_name;
l_url := l_url || '&JSESSIONID=' || OWA_COOKIE.get (name => m_jri_cookie_name_c).vals (1);
-------------------------------------------------------
-- determine cookies for calling the j2ee server
-- JSESSIONID needs to be passed, the path needs to
-- be modified for the j2ee server context
-------------------------------------------------------
get_headers_to_pass2j2ee (p_header_name_arr => l_header_name_arr, p_header_value_arr => l_header_value_arr);
-------------------------------------------------------
-- call J2EE server
-------------------------------------------------------
xlib_http.
display_url_raw (p_url => l_url, p_header_name_arr => l_header_name_arr, p_header_value_arr => l_header_value_arr);
END;
----------------------------------------------------------------------------
-- make a callout with utl_http to the j2ee container running the
-- JasperReportsIntegration webapp
-- => return the results
----------------------------------------------------------------------------
PROCEDURE show_report (p_rep_name IN VARCHAR2 DEFAULT 'test',
p_rep_format IN VARCHAR2 DEFAULT c_rep_format_pdf,
p_data_source IN VARCHAR2 DEFAULT 'default',
p_out_filename IN VARCHAR2 DEFAULT NULL,
p_rep_locale IN VARCHAR2 DEFAULT 'de_DE',
p_rep_encoding IN VARCHAR2 DEFAULT 'UTF-8',
p_additional_params IN VARCHAR2 DEFAULT NULL,
p_print_is_enabled IN BOOLEAN DEFAULT FALSE,
p_print_printer_name IN VARCHAR2 DEFAULT NULL,
p_print_media IN VARCHAR2 DEFAULT NULL,
p_print_copies IN NUMBER DEFAULT 1,
p_print_duplex IN BOOLEAN DEFAULT FALSE,
p_print_collate IN BOOLEAN DEFAULT FALSE,
p_save_is_enabled IN BOOLEAN DEFAULT FALSE,
p_save_filename IN VARCHAR2 DEFAULT NULL,
p_rep_time_zone IN VARCHAR2 DEFAULT NULL,
p_print_job_name IN VARCHAR2 DEFAULT NULL)
IS
l_proc VARCHAR2 (100) := m_module || '.SHOW_REPORT';
l_url VARCHAR2 (32767);
BEGIN
-- pick up defaults from table xlib_jasperreports_conf
setup_configuration_defaults();
-------------------------------------------------------
-- assert valid values for the input variables
-------------------------------------------------------
IF m_report_url IS NULL
THEN
xlog(p_module => m_module, p_msg => 'The report url is empty', p_type=> 'ERROR');
RAISE report_url_not_defined;
END IF;
-------------------------------------------------------
-- construct URL
-------------------------------------------------------
l_url := m_report_url;
l_url := l_url || '?_repName=' || p_rep_name;
l_url := l_url || '&_repFormat=' || p_rep_format;
l_url := l_url || '&_dataSource=' || p_data_source;
l_url := l_url || '&_outFilename=' || p_out_filename;
l_url := l_url || '&_repLocale=' || p_rep_locale;
l_url := l_url || '&_repEncoding=' || p_rep_encoding;
l_url := l_url || '&_repTimeZone=' || APEX_UTIL.URL_ENCODE (p_rep_time_zone);
-- per default use the tunnel through the database
IF m_images_uri IS NULL OR m_images_uri = c_images_uri_tunnel
THEN
-- tunnel through database
l_url := l_url || '&_imagesURI=' || APEX_UTIL.URL_ENCODE (compute_images_uri_tunnel);
ELSE
-- use parameter, mostly for direct access to servlet
l_url := l_url || '&_imagesURI=' || APEX_UTIL.URL_ENCODE (m_images_uri);
END IF;
-- direct printing
l_url := l_url || '&_printIsEnabled=' || bool2string (p_print_is_enabled);
l_url := l_url || '&_printPrinterName=' || p_print_printer_name;
l_url := l_url || '&_printPrinterTray=' || p_print_media;
l_url := l_url || '&_printCopies=' || p_print_copies;
l_url := l_url || '&_printDuplex=' || bool2string (p_print_duplex);
l_url := l_url || '&_printCollate=' || bool2string (p_print_collate);
l_url := l_url || '&_printJobName=' || p_print_job_name;
-- save file on server
l_url := l_url || '&_saveIsEnabled=' || bool2string (p_save_is_enabled);
l_url := l_url || '&_saveFileName=' || p_save_filename;
/*
Each additional parameter needs to be escaped using utl_url.escape()
utl_url.escape(
url => p_additional_params,
escape_reserved_chars => true,
url_charset => 'UTF-8'
);
*/
-- additional report parameter passed?
IF (p_additional_params IS NOT NULL)
THEN
-- l_url := l_url || '&' || p_additional_params;
l_url := l_url || '&' || utl_url.escape(
url => p_additional_params,
escape_reserved_chars => false,
url_charset => 'UTF-8'
);
END IF;
-------------------------------------------------------
-- call J2EE server
-------------------------------------------------------
xlib_http.display_url_raw (p_url => l_url);
END;
----------------------------------------------------------------------------
-- run the report and return the result as a blob
----------------------------------------------------------------------------
PROCEDURE get_report (p_rep_name IN VARCHAR2 DEFAULT 'test',
p_rep_format IN VARCHAR2 DEFAULT c_rep_format_pdf,
p_data_source IN VARCHAR2 DEFAULT 'default',
p_rep_locale IN VARCHAR2 DEFAULT 'de_DE',
p_rep_encoding IN VARCHAR2 DEFAULT 'UTF-8',
p_additional_params IN VARCHAR2 DEFAULT NULL,
p_print_is_enabled IN BOOLEAN DEFAULT FALSE,
p_print_printer_name IN VARCHAR2 DEFAULT NULL,
p_print_media IN VARCHAR2 DEFAULT NULL,
p_print_copies IN NUMBER DEFAULT 1,
p_print_duplex IN BOOLEAN DEFAULT FALSE,
p_print_collate IN BOOLEAN DEFAULT FALSE,
p_save_is_enabled IN BOOLEAN DEFAULT FALSE,
p_save_filename IN VARCHAR2 DEFAULT NULL,
p_rep_time_zone IN VARCHAR2 DEFAULT NULL,
p_out_blob IN OUT BLOB,
p_out_mime_type IN OUT VARCHAR2,
p_print_job_name IN VARCHAR2 DEFAULT NULL)
IS
l_proc VARCHAR2 (100) := m_module || '.GET_REPORT';
l_url VARCHAR2 (32767);
BEGIN
-- pick up defaults from table xlib_jasperreports_conf
setup_configuration_defaults();
-------------------------------------------------------
-- assert valid values for the input variables
-------------------------------------------------------
IF m_report_url IS NULL
THEN
RAISE report_url_not_defined;
END IF;
-------------------------------------------------------
-- construct URL
-------------------------------------------------------
l_url := m_report_url;
l_url := l_url || '?_repName=' || p_rep_name;
l_url := l_url || '&_repFormat=' || p_rep_format;
l_url := l_url || '&_dataSource=' || p_data_source;
l_url := l_url || '&_repLocale=' || p_rep_locale;
l_url := l_url || '&_repEncoding=' || p_rep_encoding;
l_url := l_url || '&_repTimeZone=' || APEX_UTIL.URL_ENCODE (p_rep_time_zone);
-- per default use the tunnel through the database
IF m_images_uri IS NULL OR m_images_uri = c_images_uri_tunnel
THEN
-- tunnel through database
l_url := l_url || '&_imagesURI=' || APEX_UTIL.URL_ENCODE (compute_images_uri_tunnel);
ELSE
-- use parameter, mostly for direct access to servlet
l_url := l_url || '&_imagesURI=' || APEX_UTIL.URL_ENCODE (m_images_uri);
END IF;
-- direct printing
l_url := l_url || '&_printIsEnabled=' || bool2string (p_print_is_enabled);
l_url := l_url || '&_printPrinterName=' || p_print_printer_name;
l_url := l_url || '&_printPrinterTray=' || p_print_media;
l_url := l_url || '&_printCopies=' || p_print_copies;
l_url := l_url || '&_printDuplex=' || bool2string (p_print_duplex);
l_url := l_url || '&_printCollate=' || bool2string (p_print_collate);
l_url := l_url || '&_printJobName=' || p_print_job_name;
-- save file on server
l_url := l_url || '&_saveIsEnabled=' || bool2string (p_save_is_enabled);
l_url := l_url || '&_saveFileName=' || p_save_filename;
-- additional report parameter passed?
IF (p_additional_params IS NOT NULL)
THEN
-- l_url := l_url || '&' || p_additional_params;
l_url := l_url || '&' || utl_url.escape(
url => p_additional_params,
escape_reserved_chars => false,
url_charset => 'UTF-8'
);
END IF;
-------------------------------------------------------
-- call Tomcat
-------------------------------------------------------
xlib_http.retrieve_blob_from_url (p_url => l_url, o_blob => p_out_blob, o_mime_type => p_out_mime_type);
END;
----------------------------------------------------------------------------
-- get default configuration
----------------------------------------------------------------------------
FUNCTION get_default_configuration
RETURN xlib_jasperreports_conf%ROWTYPE
IS
l_conf xlib_jasperreports_conf%ROWTYPE;
BEGIN
SELECT *
INTO l_conf
FROM xlib_jasperreports_conf
WHERE conf_id = 'MAIN';
RETURN l_conf;
END;
----------------------------------------------------------------------------
-- set default configuration
----------------------------------------------------------------------------
PROCEDURE set_default_configuration (p_conf IN OUT xlib_jasperreports_conf%ROWTYPE)
IS
BEGIN
p_conf.conf_id := 'MAIN';
UPDATE xlib_jasperreports_conf
SET row = p_conf
WHERE conf_id = p_conf.conf_id;
END;
PROCEDURE set_default_configuration (
p_protocol IN xlib_jasperreports_conf.conf_protocol%TYPE default 'http',
p_server IN xlib_jasperreports_conf.conf_server%TYPE default 'localhost',
p_port IN xlib_jasperreports_conf.conf_port%TYPE default '80',
p_context_path IN xlib_jasperreports_conf.conf_context_path%TYPE default 'jri',
p_wallet_path IN xlib_jasperreports_conf.conf_wallet_path%TYPE default null,
p_wallet_pwd IN xlib_jasperreports_conf.conf_wallet_pwd%TYPE default null,
p_http_transfer_timeout IN xlib_jasperreports_conf.conf_http_transfer_timeout%TYPE default 60)
IS
BEGIN
UPDATE xlib_jasperreports_conf
SET conf_protocol = p_protocol,
conf_server = p_server,
conf_port = p_port,
conf_context_path = p_context_path,
conf_wallet_path = p_wallet_path,
conf_wallet_pwd = p_wallet_pwd,
conf_http_transfer_timeout = p_http_transfer_timeout
WHERE conf_id = 'MAIN';
END;
END;```

View File

@@ -0,0 +1,63 @@
# XLIB_JASPERREPORTS_IMG
## Package Specification
```sql
PACKAGE XLIB_JASPERREPORTS_IMG AS
/*
$Id: xlib_jasperreports_img.pks 71 2017-10-15 16:25:51Z dietmar.aust $
*/
----------------------------------------------------------------------------
-- get_image: retrieves a report image from the server
----------------------------------------------------------------------------
PROCEDURE get_image (p_url in varchar2, p_image IN VARCHAR2);
procedure test;
END XLIB_JASPERREPORTS_IMG;```
## Package Body
```sql
package body xlib_jasperreports_img as
/*
$Id: xlib_jasperreports_img.pkb 71 2017-10-15 16:25:51Z dietmar.aust $
*/
m_module constant varchar2 (100) := $$plsql_unit;
----------------------------------------------------------------------------
-- get_image: retrieves a report image from the server
----------------------------------------------------------------------------
procedure get_image (p_url in varchar2, p_image in varchar2)
is
l_url varchar2 (32767);
l_proc VARCHAR2 (100) := m_module || '.GET_IMAGE';
begin
-------------------------------------------------------
-- construct URL
-------------------------------------------------------
l_url := p_url;
-- use image servlet
l_url := l_url || '_image?image=' || p_image;
-- use images directory /report_tmp/
--l_url := l_url || '_tmp/' || p_image;
-------------------------------------------------------
-- call J2EE server
-------------------------------------------------------
xlog(l_proc, l_url);
xlib_http.display_url_raw (p_url => l_url);
end;
procedure test
is
begin
htp.p('test');
end;
END XLIB_JASPERREPORTS_IMG;```

58
docs/packages/XLIB_LOG.md Normal file
View File

@@ -0,0 +1,58 @@
# XLIB_LOG
## Package Specification
```sql
PACKAGE "XLIB_LOG"
IS
/*=========================================================================
Purpose : Application Logging
License : Copyright (c) 2010 Dietmar Aust (opal-consulting.de)
Licensed under a BSD style license (license.txt)
https://github.com/daust/JasperReportsIntegration
Date Author Comment
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
06.01.2010 D. Aust Initial creation
13.05.2012 D. Aust insert fails when created_by user value too large
=========================================================================*/
PROCEDURE m (
p_module IN VARCHAR2,
p_msg IN VARCHAR2,
p_type IN VARCHAR2 DEFAULT 'DEBUG',
p_level PLS_INTEGER DEFAULT 15
);
END;```
## Package Body
```sql
PACKAGE BODY "XLIB_LOG"
IS
PROCEDURE m (
p_module IN VARCHAR2,
p_msg IN VARCHAR2,
p_type IN VARCHAR2 DEFAULT 'DEBUG',
p_level IN PLS_INTEGER DEFAULT 15
)
IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO xlib_logs
(log_module, log_msg, log_type, log_level, log_created_on, log_created_by
)
values ( substr(p_module,1,100)
, substr(p_msg,1,4000)
, substr(p_type,1,20)
, p_level
, sysdate
, substr(nvl(v('APP_USER'), user),1,100)
);
COMMIT;
END;
END;```

View File

@@ -0,0 +1,419 @@
# EVENTI_AGGIORNA_QTA_LISTA
## Codice Sorgente
```sql
PROCEDURE "EVENTI_AGGIORNA_QTA_LISTA" (
p_id_evento NUMBER
) AS
-- ricalcola tutte le qta della lista
v_cod_art VARCHAR2(10);
v_coeff_a NUMBER;
v_coeff_s NUMBER;
v_coeff_b NUMBER;
v_ospiti NUMBER;
v_qta_a NUMBER;
v_qta_s NUMBER;
v_qta_b NUMBER;
v_qta_std_a NUMBER;
v_qta_std_s NUMBER;
v_qta_std_b NUMBER;
v_count_tov NUMBER;
v_count_tov_buf NUMBER;
v_qta_giac NUMBER;
v_qta_imp NUMBER;
v_data_evt DATE;
v_qta_man VARCHAR2(100);
BEGIN
-- Daniele Viti
-- Scorre tutti gli articoli dell'evento
---------
FOR c IN (
SELECT DISTINCT
cod_articolo
FROM
eventi_det_prel
WHERE
id_evento = p_id_evento
) LOOP
BEGIN
--default su qta:
--verifica qta standard
SELECT
nvl(qta_std_a, 0),
nvl(qta_std_s, 0),
nvl(qta_std_b, 0)
INTO
v_qta_std_a,
v_qta_std_s,
v_qta_std_b
FROM
articoli
WHERE
cod_articolo = c.cod_articolo;
IF v_qta_std_a > 0 OR v_qta_std_s > 0 OR v_qta_std_b > 0 THEN
UPDATE eventi_det_prel
SET
qta_ape = v_qta_std_a,
qta_sedu = v_qta_std_s,
qta_bufdol = v_qta_std_b,
qta = qta_ape + qta_sedu + qta_bufdol + qta_man_ape + qta_man_sedu + qta_man_bufdol
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo;
COMMIT;
CONTINUE; -- se esistono le qta standard aggiorna e passa il giro successivo!
END IF;
SELECT
cod_relativo
INTO v_cod_art
FROM
articoli
WHERE
cod_articolo = c.cod_articolo;
IF v_cod_art IS NULL THEN
--Articolo Relativo assente
--verifica se cod_articolo è di tipo TOVAGLE O CARAFFE etc...
SELECT
COUNT(*)
INTO v_count_tov
FROM
articoli a
JOIN tb_codici_categ c ON a.cod_categ = c.cod_categ
JOIN tb_tipi_mat t ON c.cod_tipo = t.cod_tipo
WHERE
( c.cod_tipo IN ( 'CA-CARAFFE' )
-- or c.COD_CATEG in ('TOV-BUFF','TOV-SED')
OR c.cod_categ IN ( 'TOV-SED' ) )
AND a.cod_articolo = c.cod_articolo;
--verifica se cod_articolo è di tipo TOVAGLE BUFFET
SELECT
COUNT(*)
INTO v_count_tov_buf
FROM
articoli a
JOIN tb_codici_categ c ON a.cod_categ = c.cod_categ
JOIN tb_tipi_mat t ON c.cod_tipo = t.cod_tipo
WHERE
c.cod_categ IN ( 'TOV-BUFF' )
AND a.cod_articolo = c.cod_articolo; -- 'CM-DANI'
IF v_count_tov > 0 THEN
--c.COD_ARTICOLO è di tipo TOVAGLE O CARAFFE etc...
-- reperisco le qta tot inserita per i tavoli
SELECT
nvl(SUM(qta_ape),
0),
nvl(SUM(qta_sedu),
0),
nvl(SUM(qta_bufdol),
0)
INTO
v_qta_a,
v_qta_s,
v_qta_b
FROM
eventi_det_prel e
JOIN articoli a ON e.cod_articolo = a.cod_articolo
JOIN tb_codici_categ c ON a.cod_categ = c.cod_categ
JOIN tb_tipi_mat t ON c.cod_tipo = t.cod_tipo
WHERE
id_evento = p_id_evento
AND c.cod_tipo IN ( 'TA' );
--recupero il coefficente dell'atricolo (tovagliato o caraffe) e lo moltiplico x la qta tavoli
SELECT
nvl(coeff_a, 0),
nvl(coeff_s, 0),
nvl(coeff_b, 0)
INTO
v_coeff_a,
v_coeff_s,
v_coeff_b
FROM
articoli
WHERE
cod_articolo = c.cod_articolo;
UPDATE eventi_det_prel
SET
qta_ape = v_qta_a * v_coeff_a,
qta_sedu = v_qta_s * v_coeff_s,
qta_bufdol = v_qta_b * v_coeff_b,
qta = qta_ape + qta_sedu + qta_bufdol + qta_man_ape + qta_man_sedu + qta_man_bufdol
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo;
COMMIT;
ELSIF v_count_tov_buf > 0 THEN
--c.COD_ARTICOLO è di tipo TOVAGLE BUFFET
-- reperisco le qta tot inserita per gli angoli buffet
SELECT
nvl(SUM(qta_ape),
0),
nvl(SUM(qta_sedu),
0),
nvl(SUM(qta_bufdol),
0)
INTO
v_qta_a,
v_qta_s,
v_qta_b
FROM
eventi_det_prel e
JOIN articoli a ON e.cod_articolo = a.cod_articolo
JOIN tb_codici_categ c ON a.cod_categ = c.cod_categ
JOIN tb_tipi_mat t ON c.cod_tipo = t.cod_tipo
WHERE
id_evento = p_id_evento
AND c.cod_tipo IN ( 'AN' );
--recupero il coefficente dell'atricolo (tovagliato buffet) e lo moltiplico x la qta angoli
SELECT
nvl(coeff_a, 0),
nvl(coeff_s, 0),
nvl(coeff_b, 0)
INTO
v_coeff_a,
v_coeff_s,
v_coeff_b
FROM
articoli
WHERE
cod_articolo = c.cod_articolo;
UPDATE eventi_det_prel
SET
qta_ape = v_qta_a * v_coeff_a,
qta_sedu = v_qta_s * v_coeff_s,
qta_bufdol = v_qta_b * v_coeff_b,
qta = qta_ape + qta_sedu + qta_bufdol + qta_man_ape + qta_man_sedu + qta_man_bufdol
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo;
COMMIT;
ELSE
BEGIN
v_cod_art := c.cod_articolo;
SELECT
nvl(coeff_a, 0),
nvl(coeff_s, 0),
nvl(coeff_b, 0)
INTO
v_coeff_a,
v_coeff_s,
v_coeff_b
FROM
articoli
WHERE
cod_articolo = v_cod_art;
SELECT
nvl(tot_ospiti, 0)
INTO v_ospiti
FROM
eventi
WHERE
id = p_id_evento;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'Errore: Coefficenti/Num Ospiti NON TROVATI - '
|| sqlcode
|| ' - '
|| substr(sqlerrm, 1, 64));
END;
UPDATE eventi_det_prel
SET
qta_ape = trunc(v_coeff_a * v_ospiti),
qta_sedu = trunc(v_coeff_s * v_ospiti),
qta_bufdol = trunc(v_coeff_b * v_ospiti),
qta = qta_ape + qta_sedu + qta_bufdol + qta_man_ape + qta_man_sedu + qta_man_bufdol
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo;
COMMIT;
END IF;
ELSE
-- cod relativo valorizzato
BEGIN
-- reperisco le qta inserite per il cod_relativo nella lista
SELECT
nvl(qta_ape, 0),
nvl(qta_sedu, 0),
nvl(qta_bufdol, 0)
INTO
v_qta_a,
v_qta_s,
v_qta_b
FROM
eventi_det_prel
WHERE
cod_articolo = v_cod_art
AND id_evento = p_id_evento;
EXCEPTION
WHEN no_data_found THEN
CONTINUE;
-- Disattivato temporaneamente per evitare spam
--RAISE_APPLICATION_ERROR(-20000, 'Errore: Qta NON TROVATA !!! - v_cod_art: ' || v_cod_art || ' - ' || SQLCODE || ' - ' || SUBSTR(SQLERRM, 1 , 64));
END;
BEGIN
-- reperisco i coefficenti
SELECT
nvl(coeff_a, 0),
nvl(coeff_s, 0),
nvl(coeff_b, 0)
INTO
v_coeff_a,
v_coeff_s,
v_coeff_b
FROM
articoli
WHERE
cod_articolo = c.cod_articolo;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'Errore: Qta/Coefficenti del codice relativo NON TROVATI - '
|| sqlcode
|| ' - '
|| substr(sqlerrm, 1, 64));
END;
UPDATE eventi_det_prel
SET
qta_ape = trunc(v_coeff_a * v_qta_a),
qta_sedu = trunc(v_coeff_s * v_qta_s),
qta_bufdol = trunc(v_coeff_b * v_qta_b),
qta = qta_ape + qta_sedu + qta_bufdol + qta_man_ape + qta_man_sedu + qta_man_bufdol
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo;
COMMIT;
END IF;
-- NOTE SE SUPERO LA QTA DISPONBILE !!!
BEGIN
SELECT
data
INTO v_data_evt
FROM
eventi
WHERE
id = p_id_evento;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'v_data_evento - '
|| v_data_evt
|| ' - '
|| sqlcode
|| ' - '
|| substr(sqlerrm, 1, 64));
END;
BEGIN
SELECT
qta_giac
INTO v_qta_giac
FROM
articoli
WHERE
cod_articolo = c.cod_articolo;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'v_qta_giac - '
|| v_qta_giac
|| ' - '
|| sqlcode
|| ' - '
|| substr(sqlerrm, 1, 64));
END;
BEGIN
SELECT
nvl(f_get_qta_impegnata(c.cod_articolo, v_data_evt),
0)
INTO v_qta_imp
FROM
dual;
EXCEPTION
WHEN no_data_found THEN
raise_application_error(-20001, 'v_qta_imp - '
|| v_qta_imp
|| ' - '
|| v_cod_art
|| ', '
|| v_data_evt
|| ' - '
|| sqlcode
|| ' - '
|| substr(sqlerrm, 1, 64));
END;
--RAISE_APPLICATION_ERROR(-20000, 'v_qta_giac - ' || v_qta_giac || ' - ' || SQLCODE || ' - ' || SUBSTR(SQLERRM, 1 , 64));
dbms_output.put_line('CodArt: '
|| c.cod_articolo
|| '; Qta Imp: '
|| v_qta_imp
|| '; Qta Giac: '
|| v_qta_giac);
--NOTA: a differenza del trigger "BEFORE INSERT ON EVENTI_DET_PREL",
-- in questo caso la qta_imp è già comprensiva della qta ricalcolata
IF v_qta_imp > v_qta_giac THEN
v_qta_man := v_qta_imp - v_qta_giac;
--:NEW.NOTE := 'QTA TOT NON Disponibile, mancano: ' || to_char(v_qta_man);
UPDATE eventi_det_prel
SET
note = 'QTA TOT NON Disponibile, mancano: ' || to_char(v_qta_man)
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo;
COMMIT;
ELSE
UPDATE eventi_det_prel
SET
note = NULL
WHERE
id_evento = p_id_evento
AND cod_articolo = c.cod_articolo
AND note LIKE '%QTA TOT NON Disponibile%';
COMMIT;
END IF;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20001, 'Errore: '
|| sqlcode
|| ' - '
|| substr(sqlerrm, 1, 64));
END;
END LOOP;
RETURN;
END eventi_aggiorna_qta_lista;```

View File

@@ -0,0 +1,45 @@
# EVENTI_AGGIORNA_TOT_OSPITI
## Codice Sorgente
```sql
PROCEDURE "EVENTI_AGGIORNA_TOT_OSPITI" (
p_id_evento IN NUMBER DEFAULT 0
) AS
--aggiorna la qta toto ospiti
--job aggiorna_tot_ospiti
CURSOR c_eventi IS
SELECT
id
FROM
eventi
WHERE
id = p_id_evento;
v_tot_ospiti NUMBER;
BEGIN
/*
for c in c_eventi
loop
*/
BEGIN
v_tot_ospiti := f_get_tot_ospiti(p_id_evento);
UPDATE eventi
SET
tot_ospiti = v_tot_ospiti
WHERE
id = p_id_evento;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
raise_application_error(-20000, 'Errore nel calcolo del totale ospiti: '
|| sqlcode
|| ' - '
|| sqlerrm);
END;
-- end loop;
END eventi_aggiorna_tot_ospiti;```

View File

@@ -0,0 +1,214 @@
# EVENTI_COPIA
## Codice Sorgente
```sql
PROCEDURE eventi_copia (
id_evento_old IN NUMBER,
nuova_versione IN NUMBER DEFAULT 0,
id_evento_new OUT NUMBER
) AS
r_evento eventi%rowtype;
v_new_evt_id eventi.id%TYPE;
BEGIN
SELECT
*
INTO r_evento
FROM
eventi
WHERE
id = id_evento_old;
r_evento.id := NULL;
r_evento.is_template := 0;
IF nuova_versione = 1 THEN
r_evento.id_evt_padre := id_evento_old;
r_evento.vers_number := nvl(r_evento.vers_number, 0) + 1; -- aggiorno il numero versione
ELSE
r_evento.id_evt_padre := NULL;
r_evento.vers_number := 0;
END IF;
-- Copio l'evento facendo creare il nuovo id dal trigger
INSERT INTO eventi VALUES r_evento RETURNING id INTO v_new_evt_id;
IF nuova_versione = 1 THEN
-- Aggiorno il vecchio evento col nuovo id
UPDATE eventi
SET
id_evt_figlio = v_new_evt_id
WHERE
id = id_evento_old;
END IF;
COMMIT;
-- Aggiungo i figli alla nuova versione
-- Degustazioni
INSERT INTO eventi_det_degust (
id_evento,
data,
ora,
nome,
telefono,
email,
location,
n_persone,
menu,
n_paganti,
note,
n_degustazione,
consumata,
costo_degustazione
)
SELECT
v_new_evt_id,
data,
ora,
nome,
telefono,
email,
location,
n_persone,
menu,
n_paganti,
note,
n_degustazione,
consumata,
costo_degustazione
FROM
eventi_det_degust
WHERE
id_evento = id_evento_old;
-- Ospiti
-- Devo fare una update... DIO TRIGGER
FOR c IN (
SELECT
id_evento,
cod_tipo_ospite,
numero,
note
FROM
eventi_det_ospiti
WHERE
id_evento = id_evento_old
) LOOP
UPDATE eventi_det_ospiti
SET
numero = c.numero,
note = c.note
WHERE
id_evento = v_new_evt_id
AND cod_tipo_ospite = c.cod_tipo_ospite;
END LOOP;
-- insert into eventi_det_ospiti (ID_EVENTO, COD_TIPO_OSPITE, NUMERO, NOTE)
-- select v_new_evt_id, COD_TIPO_OSPITE, NUMERO, NOTE
-- from eventi_det_ospiti
-- where id_evento = ID_EVENTO_OLD;
-- Prelievi
/* COME DA RICHIESTA DEL 01/2024 NON COPIO LA LISTA PRELIEVO NELLA NUOVA VERSIONE */
/* RIATTIVO NUOVAMENTE LA COPIA IL 12/02/2024 */
INSERT INTO eventi_det_prel (
id_evento,
cod_articolo,
qta,
note,
qta_ape,
qta_sedu,
qta_bufdol,
qta_man_ape,
qta_man_sedu,
qta_man_bufdol,
costo_articolo
)
SELECT
v_new_evt_id,
cod_articolo,
qta,
note,
qta_ape,
qta_sedu,
qta_bufdol,
qta_man_ape,
qta_man_sedu,
qta_man_bufdol,
costo_articolo
FROM
eventi_det_prel
WHERE
id_evento = id_evento_old;
-- Risorse
INSERT INTO eventi_det_ris (
id_evento,
id_risorsa,
ore_lav,
costo,
note
)
SELECT
v_new_evt_id,
id_risorsa,
ore_lav,
costo,
note
FROM
eventi_det_ris
WHERE
id_evento = id_evento_old;
-- Costi
INSERT INTO eventi_acconti (
data,
acconto,
id_evento,
a_conferma,
ordine,
descrizione
)
SELECT
data,
acconto,
v_new_evt_id,
a_conferma,
ordine,
descrizione
FROM
eventi_acconti
WHERE
id_evento = id_evento_old;
-- Altri Costi
INSERT INTO eventi_altricosti (
id_evento,
descrizione,
costo,
quantity
)
SELECT
v_new_evt_id,
descrizione,
costo,
quantity
FROM
eventi_altricosti
WHERE
id_evento = id_evento_old;
-- Carico il nuovo id nella pagina
id_evento_new := v_new_evt_id;
EXCEPTION
WHEN OTHERS THEN
DELETE FROM eventi
WHERE
id = v_new_evt_id;
COMMIT;
raise_application_error(-20001, sqlcode
|| ' - '
|| sqlerrm);
END eventi_copia;```

View File

@@ -0,0 +1,205 @@
# EVENTI_RICALCOLA_ACCONTI
## Codice Sorgente
```sql
procedure EVENTI_RICALCOLA_ACCONTI(p_event_id number) as
v_cnt number;
v_calc_only_saldo number := 0;
v_totale_tipi number;
v_totale_degus number;
v_totale_ris number;
v_totale_ospiti number;
v_totale_evento number;
v_totale_altricosti number;
v_primo_acconto number := 0;
v_secondo_acconto number := 0;
v_terzo_acconto number := 0;
v_prima_perc number := 0.3;
v_seconda_perc number := 0.5;
v_terza_perc number := 0.2;
begin
select count(*)
into v_cnt
from eventi_acconti
where id_evento = p_event_id
and "DATA" is not null;
/*
if v_cnt > 0 then
raise_application_error(-20001, 'Impossibile ricalcolare gli acconti per un evento già saldato o parzialmente saldato');
end if;
*/
select count(*)
into v_cnt
from eventi_acconti
where id_evento = p_event_id
and (ORDINE = 10 OR ORDINE = 20) -- primo acconto (o anche secondo) dato quindi evento confermato
and "DATA" is not null;
if v_cnt > 0 then
v_calc_only_saldo := 1;
end if;
select sum(costo_ivato)
into v_totale_tipi
from get_costo_tipi_evt
where id_evento = p_event_id;
select sum(costo)
into v_totale_degus
from get_costo_degus_evt
where id_evento = p_event_id;
select sum(costo)
into v_totale_ris
from get_costo_ris_evt
where id_evento = p_event_id;
select sum(costo+costo*0.10)
into v_totale_ospiti
from get_costo_ospiti_evt
where id_evento = p_event_id;
select sum((costo * quantity)+(case when costo > 0 then costo * quantity * 0.10 else 0 end))
into v_totale_altricosti
from eventi_altricosti
where id_evento = p_event_id;
v_totale_evento :=
nvl(v_totale_tipi, 0) -
nvl(v_totale_degus, 0) +
nvl(v_totale_ris, 0) +
nvl(v_totale_ospiti, 0) +
nvl(v_totale_altricosti, 0);
if v_calc_only_saldo = 0 then
-- Se nessun acconto è stato pagato allora ricalcola tutti gli acconti
delete
from eventi_acconti
where id_evento = p_event_id
and ordine in (10, 20, 30);
insert into eventi_acconti
(DESCRIZIONE, ACCONTO, ID_EVENTO, A_CONFERMA, ORDINE)
values
('PRIMA CAPARRA (art.7 punto A del contratto) a conferma evento nella cifra di euro:',
v_totale_evento * v_prima_perc, p_event_id, 1, 10);
insert into eventi_acconti
(DESCRIZIONE, ACCONTO, ID_EVENTO, A_CONFERMA, ORDINE)
values
('SECONDA CAPARRA (art. 7 punto B - circa 60 giorni prima dell''evento) nella cifra di euro:',
v_totale_evento * v_seconda_perc, p_event_id, 0, 20);
insert into eventi_acconti
(DESCRIZIONE, ACCONTO, ID_EVENTO, A_CONFERMA, ORDINE)
values
('SALDO A RICEVIMENTO CONSUNTIVO (art.7 punto c del contratto) 5 giorni prima dell''evento',
v_totale_evento * v_terza_perc, p_event_id, 0, 30);
else
-- Controllo se gli acconti sono stato pagati e in caso ricalcolo soltanto i saldi
begin
select acconto
into v_primo_acconto
from eventi_acconti
where id_evento = p_event_id
and ORDINE = 10
and data is not null; -- Il primo acconto è stato pagato
exception when no_data_found then
v_primo_acconto := 0;
end;
begin
select acconto
into v_secondo_acconto
from eventi_acconti
where id_evento = p_event_id
and ORDINE = 20
and data is not null; -- Il secondo acconto è stato pagato
exception when no_data_found then
v_secondo_acconto := 0;
end;
begin
select acconto
into v_terzo_acconto
from eventi_acconti
where id_evento = p_event_id
and ORDINE = 30
and data is not null; -- Il terzo acconto è stato pagato
exception when no_data_found then
v_terzo_acconto := 0;
end;
-- Se non hanno pagato il primo acconto lo calcolo in automatico
if v_primo_acconto = 0 then
v_primo_acconto := v_totale_evento * v_prima_perc;
end if;
-- Ricalcolo la percentuale del secondo acconto in base al primo se non è stato pagato
if v_secondo_acconto = 0 then
v_secondo_acconto := (v_totale_evento - v_primo_acconto) * (v_seconda_perc/(v_seconda_perc + v_terza_perc));
end if;
-- Calcolo il terzo acconto come la rimanenza tra il totale e il primo + secondo se non è stato pagato
if v_terzo_acconto = 0 then
v_terzo_acconto := v_totale_evento - (v_primo_acconto + v_secondo_acconto);
end if;
-- Controllo se i totali acconti superano il totale dell'evento
if v_primo_acconto > v_totale_evento then
raise_application_error(-20001, 'Attenzione! Il primo acconto supera il costo totale del''evento');
end if;
if v_primo_acconto + v_secondo_acconto > v_totale_evento then
raise_application_error(-20001, 'Attenzione! Il primo e il secondo acconto superano il costo totale del''evento');
end if;
if v_primo_acconto + v_secondo_acconto + v_terzo_acconto > v_totale_evento then
raise_application_error(-20001, 'Attenzione! Gli acconti superano il costo totale del''evento');
end if;
-- Se gli acconti successivi sono validi allora li aggiorno, se l'acconto precendente salda tutto li elimino
if v_secondo_acconto > 0 then
update eventi_acconti
set ACCONTO = v_secondo_acconto
where id_evento = p_event_id
and ordine = 20;
if SQL%ROWCOUNT = 0 then
insert into eventi_acconti
(DESCRIZIONE, ACCONTO, ID_EVENTO, A_CONFERMA, ORDINE)
values
('SECONDA CAPARRA (art. 7 punto B - circa 60 giorni prima dell''evento) nella cifra di euro:',
v_secondo_acconto, p_event_id, 0, 20);
end if;
else
delete
from eventi_acconti
where id_evento = p_event_id
and ordine = 20;
end if;
if v_terzo_acconto > 0 then
update eventi_acconti
set ACCONTO = v_terzo_acconto
where id_evento = p_event_id
and ordine = 30;
if SQL%ROWCOUNT = 0 then
insert into eventi_acconti
(DESCRIZIONE, ACCONTO, ID_EVENTO, A_CONFERMA, ORDINE)
values
('SALDO A RICEVIMENTO CONSUNTIVO (art.7 punto c del contratto) ', v_terzo_acconto, p_event_id, 0, 30);
end if;
else
delete
from eventi_acconti
where id_evento = p_event_id
and ordine = 30;
end if;
end if;
end;```

View File

@@ -0,0 +1,12 @@
# EVENTO_ELIMINA_PRELIEVI
## Codice Sorgente
```sql
PROCEDURE EVENTO_ELIMINA_PRELIEVI
(
P_ID_EVENTO IN NUMBER
) AS
BEGIN
NULL;
END EVENTO_ELIMINA_PRELIEVI;```

22
docs/procedures/HTPPRN.md Normal file
View File

@@ -0,0 +1,22 @@
# HTPPRN
## Codice Sorgente
```sql
PROCEDURE "HTPPRN" (pclob in out nocopy clob) is
v_excel varchar2(32000);
v_clob clob := pclob;
begin
while length(v_clob) > 0 loop begin
if length(v_clob) > 32000 then v_excel:= substr(v_clob,1,32000);
htp.prn(v_excel);
v_clob:= substr(v_clob,length(v_excel)+1);
else
v_excel := v_clob;
htp.prn(v_excel);
v_clob:=''; v_excel := ''; end if;
end;
end loop;
end;
```

View File

@@ -0,0 +1,308 @@
# LISTE_COPIA
## Codice Sorgente
```sql
PROCEDURE liste_copia (
id_evento_from IN NUMBER,
id_evento_to IN NUMBER,
copia_degustazioni IN NUMBER DEFAULT 0,
copia_prelievi IN NUMBER DEFAULT 0,
copia_risorse IN NUMBER DEFAULT 0,
copia_acconti IN NUMBER DEFAULT 0,
copia_altricosti IN NUMBER DEFAULT 0
) AS
BEGIN
-- Validate that mandatory parameters are provided
IF id_evento_from IS NULL OR id_evento_to IS NULL THEN
RAISE_APPLICATION_ERROR(-20001, 'Both id_evento_from and id_evento_to must be provided.');
END IF;
----------------------------------------------------------------------------
-- Degustazioni (Tastings)
----------------------------------------------------------------------------
IF copia_degustazioni > 0 THEN
FOR rec IN (
SELECT
id,
data,
ora,
nome,
telefono,
email,
location,
n_persone,
menu,
n_paganti,
note,
n_degustazione,
consumata,
costo_degustazione
FROM eventi_det_degust
WHERE id_evento = id_evento_from
) LOOP
-- Try to update using the unique key (assumed here as n_degustazione)
UPDATE eventi_det_degust
SET
data = rec.data,
ora = rec.ora,
nome = rec.nome,
telefono = rec.telefono,
email = rec.email,
location = rec.location,
n_persone = rec.n_persone,
menu = rec.menu,
n_paganti = rec.n_paganti,
note = rec.note,
consumata = rec.consumata,
costo_degustazione = rec.costo_degustazione
WHERE id_evento = id_evento_to
AND id = rec.id;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO eventi_det_degust (
id_evento,
data,
ora,
nome,
telefono,
email,
location,
n_persone,
menu,
n_paganti,
note,
n_degustazione,
consumata,
costo_degustazione
)
VALUES (
id_evento_to,
rec.data,
rec.ora,
rec.nome,
rec.telefono,
rec.email,
rec.location,
rec.n_persone,
rec.menu,
rec.n_paganti,
rec.note,
rec.n_degustazione,
rec.consumata,
rec.costo_degustazione
);
END IF;
END LOOP;
----------------------------------------------------------------------------
-- Ospiti (Guests)
----------------------------------------------------------------------------
FOR rec IN (
SELECT
cod_tipo_ospite,
numero,
note
FROM eventi_det_ospiti
WHERE id_evento = id_evento_from
) LOOP
UPDATE eventi_det_ospiti
SET
numero = rec.numero,
note = rec.note
WHERE id_evento = id_evento_to
AND cod_tipo_ospite = rec.cod_tipo_ospite;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO eventi_det_ospiti (
id_evento,
cod_tipo_ospite,
numero,
note
)
VALUES (
id_evento_to,
rec.cod_tipo_ospite,
rec.numero,
rec.note
);
END IF;
END LOOP;
END IF;
----------------------------------------------------------------------------
-- Prelievi (Sampling): inserisce solo i record mancanti
----------------------------------------------------------------------------
IF copia_prelievi > 0 THEN
MERGE INTO eventi_det_prel dest
USING (
SELECT
cod_articolo,
qta,
note,
qta_ape,
qta_sedu,
qta_bufdol,
qta_man_ape,
qta_man_sedu,
qta_man_bufdol,
costo_articolo
FROM eventi_det_prel
WHERE id_evento = id_evento_from
) src
ON (
dest.id_evento = id_evento_to
AND dest.cod_articolo = src.cod_articolo
)
WHEN NOT MATCHED THEN
INSERT (
id_evento,
cod_articolo,
qta,
note,
qta_ape,
qta_sedu,
qta_bufdol,
qta_man_ape,
qta_man_sedu,
qta_man_bufdol,
costo_articolo
)
VALUES (
id_evento_to,
src.cod_articolo,
src.qta,
src.note,
src.qta_ape,
src.qta_sedu,
src.qta_bufdol,
src.qta_man_ape,
src.qta_man_sedu,
src.qta_man_bufdol,
src.costo_articolo
);
END IF;
----------------------------------------------------------------------------
-- Risorse (Resources)
----------------------------------------------------------------------------
IF copia_risorse > 0 THEN
FOR rec IN (
SELECT
id,
id_risorsa,
ore_lav,
costo,
note
FROM eventi_det_ris
WHERE id_evento = id_evento_from
) LOOP
UPDATE eventi_det_ris
SET
ore_lav = rec.ore_lav,
costo = rec.costo,
note = rec.note
WHERE id_evento = id_evento_to
AND id = rec.id;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO eventi_det_ris (
id_evento,
id_risorsa,
ore_lav,
costo,
note
)
VALUES (
id_evento_to,
rec.id_risorsa,
rec.ore_lav,
rec.costo,
rec.note
);
END IF;
END LOOP;
END IF;
----------------------------------------------------------------------------
-- Acconti (Payments)
----------------------------------------------------------------------------
IF copia_acconti > 0 THEN
FOR rec IN (
SELECT
id,
data,
acconto,
a_conferma,
ordine,
descrizione
FROM eventi_acconti
WHERE id_evento = id_evento_from
) LOOP
UPDATE eventi_acconti
SET
data = rec.data,
acconto = rec.acconto,
a_conferma = rec.a_conferma,
descrizione = rec.descrizione
WHERE id_evento = id_evento_to
AND id = rec.id;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO eventi_acconti (
data,
acconto,
id_evento,
a_conferma,
ordine,
descrizione
)
VALUES (
rec.data,
rec.acconto,
id_evento_to,
rec.a_conferma,
rec.ordine,
rec.descrizione
);
END IF;
END LOOP;
END IF;
----------------------------------------------------------------------------
-- Altri Costi (Other Costs)
----------------------------------------------------------------------------
IF copia_altricosti > 0 THEN
FOR rec IN (
SELECT
id,
descrizione,
costo,
quantity
FROM eventi_altricosti
WHERE id_evento = id_evento_from
) LOOP
UPDATE eventi_altricosti
SET
costo = rec.costo,
quantity = rec.quantity
WHERE id_evento = id_evento_to
AND id = rec.id;
IF SQL%ROWCOUNT = 0 THEN
INSERT INTO eventi_altricosti (
id_evento,
descrizione,
costo,
quantity
)
VALUES (
id_evento_to,
rec.descrizione,
rec.costo,
rec.quantity
);
END IF;
END LOOP;
END IF;
END liste_copia;```

View File

@@ -0,0 +1,41 @@
# P_CANCEL_SAME_LOCATION_EVENTS
## Codice Sorgente
```sql
PROCEDURE "P_CANCEL_SAME_LOCATION_EVENTS" (p_good_event_id number) as
-- ---------------------------
-- Autore: Daniele Viti
-- Data: 17/01/2020
-- Descrizione: Controlla che ci siano più eventi con la stessa locazione e data di quello in esame e li annulla
-- ---------------------------
v_date date;
v_idloc number;
begin
begin
-- Seleziono la data e la location per confrontarle dopo
select data, id_location
into v_date, v_idloc
from eventi
where id = p_good_event_id;
exception when no_data_found then
raise_application_error(-20001, 'Impossibile trovare l''evento da confermare');
end;
for c in (
select *
from eventi
where data = v_date
and id_location = v_idloc
and id != p_good_event_id
) loop
-- Imposta l'evento come obsoleto (annullato)
UPDATE EVENTI
SET
FLG_SUPERATO = 1,
STATO = 900
WHERE ID = c.id;
end loop;
end;```

90
docs/procedures/README.md Normal file
View File

@@ -0,0 +1,90 @@
# Stored Procedures
Questa cartella contiene la documentazione di tutte le 11 stored procedures del database.
## Procedures Business Logic
| Procedura | Descrizione |
|-----------|-------------|
| [EVENTI_AGGIORNA_QTA_LISTA](EVENTI_AGGIORNA_QTA_LISTA.md) | Ricalcola tutte le quantità della lista prelievo |
| [EVENTI_AGGIORNA_TOT_OSPITI](EVENTI_AGGIORNA_TOT_OSPITI.md) | Aggiorna il totale ospiti in testata evento |
| [EVENTI_COPIA](EVENTI_COPIA.md) | Duplica un evento con tutti i dettagli |
| [EVENTI_RICALCOLA_ACCONTI](EVENTI_RICALCOLA_ACCONTI.md) | Ricalcola gli importi degli acconti |
| [EVENTO_ELIMINA_PRELIEVI](EVENTO_ELIMINA_PRELIEVI.md) | Elimina i prelievi di un evento |
| [LISTE_COPIA](LISTE_COPIA.md) | Copia selettiva di liste tra due eventi |
| [P_CANCEL_SAME_LOCATION_EVENTS](P_CANCEL_SAME_LOCATION_EVENTS.md) | Annulla eventi nella stessa location/data |
## Procedures Utility
| Procedura | Descrizione |
|-----------|-------------|
| [ROWSORT_TIPI](ROWSORT_TIPI.md) | Riordina i tipi materiale |
| [HTPPRN](HTPPRN.md) | Output HTTP per APEX |
| [SEND_DATA_TO_DROPBOX](SEND_DATA_TO_DROPBOX.md) | Export dati verso Dropbox |
| [XLOG](XLOG.md) | Procedura di logging |
## Dettaglio Procedures Critiche
### EVENTI_AGGIORNA_QTA_LISTA
**Parametri:**
- `p_id_evento NUMBER` - ID dell'evento
**Logica:**
1. Cicla su tutti gli articoli nella lista prelievo
2. Per ogni articolo determina il metodo di calcolo:
- **Quantità Standard**: Se `QTA_STD_A/S/B` sono valorizzate
- **Tovagliato/Caraffe**: Moltiplica per quantità tavoli/angoli
- **Codice Relativo**: Moltiplica per quantità articolo di riferimento
- **Standard**: Moltiplica coefficienti per totale ospiti
3. Verifica disponibilità rispetto alla giacenza
4. Aggiorna note con warning se quantità insufficiente
### EVENTI_COPIA
**Parametri:**
- `id_evento_old NUMBER` - ID evento da copiare
- `nuova_versione NUMBER DEFAULT 0` - 1 per creare nuova versione
- `id_evento_new OUT NUMBER` - ID nuovo evento creato
**Logica:**
1. Copia testata evento
2. Se nuova versione: imposta `ID_EVT_PADRE`, incrementa `VERS_NUMBER`
3. Aggiorna evento vecchio con `ID_EVT_FIGLIO`
4. Copia tutte le tabelle dettaglio:
- `EVENTI_DET_DEGUST`
- `EVENTI_DET_OSPITI` (via UPDATE, non INSERT)
- `EVENTI_DET_PREL`
- `EVENTI_DET_RIS`
- `EVENTI_ACCONTI`
- `EVENTI_ALTRICOSTI`
5. In caso di errore: elimina evento creato e solleva eccezione
### EVENTI_RICALCOLA_ACCONTI
**Parametri:**
- `p_event_id NUMBER` - ID evento
**Logica:**
1. Calcola totale evento dalle viste costo
2. Se nessun acconto pagato:
- Elimina acconti esistenti
- Inserisce nuovi acconti con percentuali 30%-50%-20%
3. Se acconti parzialmente pagati:
- Mantiene importi pagati
- Ricalcola solo acconti non pagati
- Verifica che totale acconti ≤ totale evento
### LISTE_COPIA
**Parametri:**
- `id_evento_from NUMBER` - Evento sorgente
- `id_evento_to NUMBER` - Evento destinazione
- `copia_degustazioni NUMBER DEFAULT 0`
- `copia_prelievi NUMBER DEFAULT 0`
- `copia_risorse NUMBER DEFAULT 0`
- `copia_acconti NUMBER DEFAULT 0`
- `copia_altricosti NUMBER DEFAULT 0`
**Logica:**
Per ogni flag > 0 esegue un MERGE (UPDATE se esiste, INSERT altrimenti).

Some files were not shown because too many files have changed in this diff Show More