Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 455b6f19bf | |||
| 040708a2db |
+7
-14
@@ -1,16 +1,16 @@
|
||||
APP_ENV=production
|
||||
APP_DEBUG=true
|
||||
APP_KEY=base64:C+sutHm6xP5sE4QXhoZFhYjArlVN11s2mDU1F8beUkM=
|
||||
APP_DEBUG=false
|
||||
APP_KEY=
|
||||
APP_URL=http://vanguard.test
|
||||
|
||||
LOG_CHANNEL=stack
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST="localhost"
|
||||
DB_DATABASE="xxxx"
|
||||
DB_USERNAME="xxxx"
|
||||
DB_PASSWORD="xxxxx"
|
||||
DB_PREFIX="auth_"
|
||||
DB_HOST=localhost
|
||||
DB_DATABASE=vanguard
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
DB_PREFIX=vg_
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
@@ -39,10 +39,3 @@ PUSHER_APP_CLUSTER=mt1
|
||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
# Credenziali API VisualLims
|
||||
SIMULATE_EXPORT_LIMS=true
|
||||
API_BASE_URL=https://bvcpsitaly-elims.com/limsapi
|
||||
API_USERNAME=WebApiUser
|
||||
API_PASSWORD=webapiuser01
|
||||
|
||||
BASE_URL=http://localhost:8000/userarea/
|
||||
+29
-39
@@ -1,64 +1,54 @@
|
||||
.DS_Store
|
||||
|
||||
/node_modules
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/vendor
|
||||
|
||||
/.idea
|
||||
/.fleet
|
||||
/.vscode
|
||||
/.vagrant
|
||||
|
||||
/public/hot
|
||||
/public/storage
|
||||
/public/build
|
||||
|
||||
/storage/*.key
|
||||
|
||||
Homestead.json
|
||||
Homestead.yaml
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
.env
|
||||
.phpunit.result.cache
|
||||
.php_cs.cache
|
||||
/documentation
|
||||
/.phpunit.cache
|
||||
/public/build
|
||||
.env.backup
|
||||
.env.production
|
||||
auth.json
|
||||
|
||||
.phpunit.result.cache
|
||||
.php_cs.cache
|
||||
/.phpunit.cache
|
||||
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
/documentation
|
||||
|
||||
# --- Runtime / Debug (userarea) ---
|
||||
# File di debug e temporanei JSON e log
|
||||
/public/userarea/*.json
|
||||
/public/userarea/*.log
|
||||
/public/userarea/*.txt
|
||||
/public/userarea/*_response.json
|
||||
/public/userarea/customfield_values_response.json
|
||||
/public/userarea/error_log.txt
|
||||
/public/userarea/import_debug.log
|
||||
/public/userarea/last_url.txt
|
||||
/public/userarea/logaspi/
|
||||
/public/userarea/logs/api/
|
||||
/public/userarea/logs/api/**
|
||||
/public/userarea/logs/
|
||||
/public/userarea/logs/**
|
||||
/public/userarea/photostrf/
|
||||
|
||||
# File di log nella sottocartella class
|
||||
/public/userarea/class/*.log
|
||||
/public/userarea/class/curl_auth_debug.log
|
||||
/public/userarea/class/curl_request_debug.log
|
||||
/public/userarea/schema_dettagli_response.json
|
||||
public/userarea/schemi_base_response.json
|
||||
|
||||
# File XLSX temporanei importati
|
||||
/public/userarea/imported_trf/*.xlsx
|
||||
/public/userarea/xlstemplates/*.xlsx
|
||||
|
||||
# Cartelle foto generate
|
||||
# Ignora cartelle di foto generate
|
||||
/public/photostrf/
|
||||
/public/photostrf/qrcodes/
|
||||
public/userarea/import_debug.log
|
||||
public/userarea/last_url.txt
|
||||
public/userarea/class/curl_auth_debug.log
|
||||
public/userarea/class/curl_request_debug.log
|
||||
public/userarea/last_url.txt
|
||||
public/userarea/class/curl_auth_debug.log
|
||||
public/userarea/class/curl_request_debug.log
|
||||
|
||||
# Ignora tutti i log ovunque
|
||||
# Ignora tutti i log
|
||||
*.log
|
||||
|
||||
public/userarea/cache/
|
||||
public/userarea/error_log.txt
|
||||
|
||||
# Ignora cartella photostrf in public/userarea
|
||||
/public/userarea/photostrf/
|
||||
public/userarea/customfield_values_response.json
|
||||
|
||||
|
||||
@@ -109,8 +109,6 @@ class LoginController extends Controller
|
||||
// Reindirizza in base al ruolo
|
||||
if ($user->hasRole('Admin')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
} elseif ($user->hasRole('SuperUser')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
} elseif ($user->hasRole('User')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
}
|
||||
|
||||
@@ -34,8 +34,13 @@ class EventServiceProvider extends ServiceProvider
|
||||
Verified::class => [
|
||||
ActivateUser::class,
|
||||
],
|
||||
<<<<<<< HEAD
|
||||
\SocialiteProviders\Manager\SocialiteWasMapped::class => [
|
||||
\SocialiteProviders\Microsoft\MicrosoftExtendSocialite::class,
|
||||
=======
|
||||
\SocialiteProviders\Manager\SocialiteWasCalled::class => [
|
||||
'SocialiteProviders\\Azure\\AzureExtendSocialite@handle',
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
],
|
||||
];
|
||||
|
||||
@@ -46,7 +51,7 @@ class EventServiceProvider extends ServiceProvider
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
//
|
||||
parent::boot(); // Aggiungi questa linea per sicurezza
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
-1
@@ -44,8 +44,11 @@
|
||||
"phpmailer/phpmailer": "^6.9",
|
||||
"phpoffice/phpspreadsheet": "^4.1",
|
||||
"proengsoft/laravel-jsvalidation": "^4.0.0",
|
||||
"robmorgan/phinx": "^0.16.12",
|
||||
<<<<<<< HEAD
|
||||
"socialiteproviders/microsoft": "^4.7",
|
||||
=======
|
||||
"socialiteproviders/microsoft-azure": "^5.2",
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
"spatie/laravel-query-builder": "^5.0",
|
||||
"vanguardapp/activity-log": "^6.0",
|
||||
"vanguardapp/announcements": "^6.0",
|
||||
|
||||
Generated
+45
-646
@@ -4,7 +4,11 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "557df76fdde549210349be369d479f5d",
|
||||
<<<<<<< HEAD
|
||||
"content-hash": "9c4f1e3bc3ee2180211c055e70635aef",
|
||||
=======
|
||||
"content-hash": "583c865e5b67680202a816953a662b03",
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
"packages": [
|
||||
{
|
||||
"name": "akaunting/laravel-setting",
|
||||
@@ -251,330 +255,6 @@
|
||||
],
|
||||
"time": "2023-11-29T23:19:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/chronos",
|
||||
"version": "3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/chronos.git",
|
||||
"reference": "e6e777b534244911566face8a5dbdbd7f7bda5a6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/chronos/zipball/e6e777b534244911566face8a5dbdbd7f7bda5a6",
|
||||
"reference": "e6e777b534244911566face8a5dbdbd7f7bda5a6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"psr/clock": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/clock-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cakephp-codesniffer": "^5.0",
|
||||
"phpunit/phpunit": "^10.5.58 || ^11.5.3 || ^12.1.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Chronos\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Brian Nesbitt",
|
||||
"email": "brian@nesbot.com",
|
||||
"homepage": "http://nesbot.com"
|
||||
},
|
||||
{
|
||||
"name": "The CakePHP Team",
|
||||
"homepage": "https://cakephp.org"
|
||||
}
|
||||
],
|
||||
"description": "A simple API extension for DateTime.",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"date",
|
||||
"datetime",
|
||||
"time"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/chronos/issues",
|
||||
"source": "https://github.com/cakephp/chronos"
|
||||
},
|
||||
"time": "2026-04-10T02:50:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/core",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/core.git",
|
||||
"reference": "9c458b0e9322ec88bc4c758b33cde6a0abf49d12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/core/zipball/9c458b0e9322ec88bc4c758b33cde6a0abf49d12",
|
||||
"reference": "9c458b0e9322ec88bc4c758b33cde6a0abf49d12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/utility": "^5.3.0",
|
||||
"league/container": "^5.1",
|
||||
"php": ">=8.2",
|
||||
"psr/container": "^1.1 || ^2.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/cache": "To use Configure::store() and restore().",
|
||||
"cakephp/event": "To use PluginApplicationInterface or plugin applications.",
|
||||
"league/container": "To use Container and ServiceProvider classes"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Cake\\Core\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/core/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "CakePHP Framework Core classes",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"core",
|
||||
"framework"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/core"
|
||||
},
|
||||
"time": "2026-05-15T03:31:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/database",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/database.git",
|
||||
"reference": "442d12bf0a1edeffc555a59de9f955cb0619c7ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/database/zipball/442d12bf0a1edeffc555a59de9f955cb0619c7ca",
|
||||
"reference": "442d12bf0a1edeffc555a59de9f955cb0619c7ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/chronos": "^3.3",
|
||||
"cakephp/core": "^5.3.0",
|
||||
"cakephp/datasource": "^5.3.0",
|
||||
"php": ">=8.2",
|
||||
"psr/log": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/i18n": "^5.3.0",
|
||||
"cakephp/log": "^5.3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/i18n": "If you are using locale-aware datetime formats.",
|
||||
"cakephp/log": "If you want to use query logging without providing a logger yourself."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Database\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/database/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Flexible and powerful Database abstraction library with a familiar PDO-like API",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"abstraction",
|
||||
"cakephp",
|
||||
"database",
|
||||
"database abstraction",
|
||||
"pdo"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/database"
|
||||
},
|
||||
"time": "2026-05-21T19:38:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/datasource",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/datasource.git",
|
||||
"reference": "b768f0c0bf0fca815f83c4caef14e7fbb2409bf5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/datasource/zipball/b768f0c0bf0fca815f83c4caef14e7fbb2409bf5",
|
||||
"reference": "b768f0c0bf0fca815f83c4caef14e7fbb2409bf5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/core": "^5.3.0",
|
||||
"php": ">=8.2",
|
||||
"psr/simple-cache": "^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cache": "^5.3.0",
|
||||
"cakephp/collection": "^5.3.0",
|
||||
"cakephp/utility": "^5.3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/cache": "If you decide to use Query caching.",
|
||||
"cakephp/collection": "If you decide to use ResultSetInterface.",
|
||||
"cakephp/utility": "If you decide to use EntityTrait."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Datasource\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/datasource/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"connection management",
|
||||
"datasource",
|
||||
"entity",
|
||||
"query"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/datasource"
|
||||
},
|
||||
"time": "2026-05-20T10:27:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/utility",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/utility.git",
|
||||
"reference": "4c703a010b9d955fed44731669e35d3043425cc7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/utility/zipball/4c703a010b9d955fed44731669e35d3043425cc7",
|
||||
"reference": "4c703a010b9d955fed44731669e35d3043425cc7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/core": "^5.3.0",
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "To use Text::transliterate() or Text::slug()",
|
||||
"lib-ICU": "To use Text::transliterate() or Text::slug()"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Cake\\Utility\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/utility/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "CakePHP Utility classes such as Inflector, String, Hash, and Security",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"hash",
|
||||
"inflector",
|
||||
"security",
|
||||
"string",
|
||||
"utility"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/utility"
|
||||
},
|
||||
"time": "2026-05-21T19:38:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
"version": "3.2.0",
|
||||
@@ -2951,90 +2631,6 @@
|
||||
],
|
||||
"time": "2022-12-11T20:36:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/container",
|
||||
"version": "5.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/container.git",
|
||||
"reference": "58accbc032f0090a9bd08326f93062c5a658b2c5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/container/zipball/58accbc032f0090a9bd08326f93062c5a658b2c5",
|
||||
"reference": "58accbc032f0090a9bd08326f93062c5a658b2c5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"psr/container": "^2.0.2",
|
||||
"psr/event-dispatcher": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"orno/di": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/php-generator": "^4.1",
|
||||
"nikic/php-parser": "^5.0",
|
||||
"phpstan/phpstan": "^2.1.11",
|
||||
"phpunit/phpunit": "^10.5.45|^11.5.15|^12.0",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"scrutinizer/ocular": "^1.9",
|
||||
"squizlabs/php_codesniffer": "^3.9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-1.x": "1.x-dev",
|
||||
"dev-2.x": "2.x-dev",
|
||||
"dev-3.x": "3.x-dev",
|
||||
"dev-4.x": "4.x-dev",
|
||||
"dev-5.x": "5.x-dev",
|
||||
"dev-master": "5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Container\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Bennett",
|
||||
"email": "mail@philbennett.co.uk",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A fast and intuitive dependency injection container.",
|
||||
"homepage": "https://github.com/thephpleague/container",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency",
|
||||
"di",
|
||||
"injection",
|
||||
"league",
|
||||
"provider",
|
||||
"service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/container/issues",
|
||||
"source": "https://github.com/thephpleague/container/tree/5.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/philipobenito",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-19T18:52:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "3.28.0",
|
||||
@@ -5388,93 +4984,6 @@
|
||||
],
|
||||
"time": "2024-04-27T21:32:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "robmorgan/phinx",
|
||||
"version": "0.16.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/phinx.git",
|
||||
"reference": "1cce44387a9146dc04c312e6a231b17fb30385cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/phinx/zipball/1cce44387a9146dc04c312e6a231b17fb30385cd",
|
||||
"reference": "1cce44387a9146dc04c312e6a231b17fb30385cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/database": "^5.0.2",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"php-64bit": ">=8.1",
|
||||
"psr/container": "^1.1|^2.0",
|
||||
"symfony/config": "^4.0|^5.0|^6.0|^7.0|^8.0",
|
||||
"symfony/console": "^6.0|^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cakephp-codesniffer": "^5.0",
|
||||
"cakephp/i18n": "^5.0",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"symfony/yaml": "^4.0|^5.0|^6.0|^7.0|^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-json": "Install if using JSON configuration format",
|
||||
"ext-pdo": "PDO extension is needed",
|
||||
"symfony/yaml": "Install if using YAML configuration format"
|
||||
},
|
||||
"bin": [
|
||||
"bin/phinx"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phinx\\": "src/Phinx/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Rob Morgan",
|
||||
"email": "robbym@gmail.com",
|
||||
"homepage": "https://robmorgan.id.au",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"email": "woody.gilk@gmail.com",
|
||||
"homepage": "https://shadowhand.me",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Richard Quadling",
|
||||
"email": "rquadling@gmail.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/phinx/graphs/contributors",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
|
||||
"homepage": "https://phinx.org",
|
||||
"keywords": [
|
||||
"database",
|
||||
"database migrations",
|
||||
"db",
|
||||
"migrations",
|
||||
"phinx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/phinx/issues",
|
||||
"source": "https://github.com/cakephp/phinx/tree/0.16.12"
|
||||
},
|
||||
"time": "2026-07-03T07:27:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "socialiteproviders/manager",
|
||||
"version": "v4.8.1",
|
||||
@@ -5550,6 +5059,7 @@
|
||||
"time": "2025-02-24T19:33:30+00:00"
|
||||
},
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
"name": "socialiteproviders/microsoft",
|
||||
"version": "4.7.0",
|
||||
"source": {
|
||||
@@ -5561,18 +5071,38 @@
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SocialiteProviders/Microsoft/zipball/824ef97a4f6e3f363c21702b76676d54e8265573",
|
||||
"reference": "824ef97a4f6e3f363c21702b76676d54e8265573",
|
||||
=======
|
||||
"name": "socialiteproviders/microsoft-azure",
|
||||
"version": "5.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/SocialiteProviders/Microsoft-Azure.git",
|
||||
"reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/SocialiteProviders/Microsoft-Azure/zipball/453d62c9d7e3b3b76e94c913fb46e68a33347b16",
|
||||
"reference": "453d62c9d7e3b3b76e94c913fb46e68a33347b16",
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-json": "*",
|
||||
<<<<<<< HEAD
|
||||
"firebase/php-jwt": "^6.8",
|
||||
=======
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
"php": "^8.0",
|
||||
"socialiteproviders/manager": "^4.4"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
<<<<<<< HEAD
|
||||
"SocialiteProviders\\Microsoft\\": ""
|
||||
=======
|
||||
"SocialiteProviders\\Azure\\": ""
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
@@ -5581,12 +5111,22 @@
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
<<<<<<< HEAD
|
||||
"name": "Brian Faust",
|
||||
"email": "hello@brianfaust.de"
|
||||
}
|
||||
],
|
||||
"description": "Microsoft OAuth2 Provider for Laravel Socialite",
|
||||
"keywords": [
|
||||
=======
|
||||
"name": "Chris Hemmings",
|
||||
"email": "chris@hemmin.gs"
|
||||
}
|
||||
],
|
||||
"description": "Microsoft Azure OAuth2 Provider for Laravel Socialite",
|
||||
"keywords": [
|
||||
"azure",
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
"laravel",
|
||||
"microsoft",
|
||||
"oauth",
|
||||
@@ -5594,11 +5134,19 @@
|
||||
"socialite"
|
||||
],
|
||||
"support": {
|
||||
<<<<<<< HEAD
|
||||
"docs": "https://socialiteproviders.com/microsoft",
|
||||
"issues": "https://github.com/socialiteproviders/providers/issues",
|
||||
"source": "https://github.com/socialiteproviders/providers"
|
||||
},
|
||||
"time": "2025-07-06T00:25:25+00:00"
|
||||
=======
|
||||
"docs": "https://socialiteproviders.com/microsoft-azure",
|
||||
"issues": "https://github.com/socialiteproviders/providers/issues",
|
||||
"source": "https://github.com/socialiteproviders/providers"
|
||||
},
|
||||
"time": "2024-03-15T03:02:10+00:00"
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
},
|
||||
{
|
||||
"name": "spatie/laravel-package-tools",
|
||||
@@ -5807,85 +5355,6 @@
|
||||
],
|
||||
"time": "2024-05-31T14:57:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v7.4.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
|
||||
"reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/filesystem": "^7.1|^8.0",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/finder": "<6.4",
|
||||
"symfony/service-contracts": "<2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/event-dispatcher": "^6.4|^7.0|^8.0",
|
||||
"symfony/finder": "^6.4|^7.0|^8.0",
|
||||
"symfony/messenger": "^6.4|^7.0|^8.0",
|
||||
"symfony/service-contracts": "^2.5|^3",
|
||||
"symfony/yaml": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Config\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/config/tree/v7.4.14"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-06-09T07:51:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.1.3",
|
||||
@@ -6342,76 +5811,6 @@
|
||||
],
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v7.4.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50",
|
||||
"reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-mbstring": "~1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/process": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Filesystem\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v7.4.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-05-11T16:38:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v7.1.3",
|
||||
@@ -11999,6 +11398,6 @@
|
||||
"php": "^8.2.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": {},
|
||||
"platform-dev": [],
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
+5
-1
@@ -12,7 +12,11 @@ return [
|
||||
*/
|
||||
|
||||
'social' => [
|
||||
'providers' => ['microsoft'],
|
||||
<<<<<<< HEAD
|
||||
'providers' => ['facebook', 'twitter', 'google', 'microsoft'],
|
||||
=======
|
||||
'providers' => ['azure'],
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
],
|
||||
|
||||
/*
|
||||
|
||||
@@ -64,12 +64,15 @@ return [
|
||||
'redirect' => env('GOOGLE_CALLBACK_URI'),
|
||||
],
|
||||
|
||||
<<<<<<< HEAD
|
||||
'microsoft' => [
|
||||
'client_id' => env('MICROSOFT_CLIENT_ID'),
|
||||
'client_secret' => env('MICROSOFT_CLIENT_SECRET'),
|
||||
'redirect' => env('MICROSOFT_REDIRECT_URI'),
|
||||
],
|
||||
|
||||
=======
|
||||
>>>>>>> 040708a2dbf9850937d72bcdb4aba264fe9b9f4c
|
||||
// 'authy' => [
|
||||
// 'key' => env('AUTHY_KEY'),
|
||||
// ],
|
||||
@@ -84,4 +87,11 @@ return [
|
||||
'channel' => env('SLACK_BOT_USER_DEFAULT_CHANNEL'),
|
||||
],
|
||||
],
|
||||
|
||||
'azure' => [
|
||||
'client_id' => env('AZURE_CLIENT_ID'),
|
||||
'client_secret' => env('AZURE_CLIENT_SECRET'),
|
||||
'redirect' => env('AZURE_REDIRECT_URI'),
|
||||
'tenant' => env('AZURE_TENANT_ID'),
|
||||
],
|
||||
];
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
<?php
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreatePhinxTestTable extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('phinx_test');
|
||||
$table
|
||||
->addColumn('name', 'string', ['limit' => 100, 'null' => false])
|
||||
->addColumn('created_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddAddnoteToTemplateMapping extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('template_mapping');
|
||||
$table->addColumn('is_addnote', 'boolean', [
|
||||
'default' => 0,
|
||||
'null' => false,
|
||||
'after' => 'is_visible_parts',
|
||||
'comment' => 'Campo utilizzabile come addnote (multi-selezione)',
|
||||
])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreateOptionsTable extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('options');
|
||||
$table
|
||||
->addColumn('opt_key', 'string', ['limit' => 100])
|
||||
->addColumn('opt_value', 'text', ['null' => true])
|
||||
->addColumn('created_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
|
||||
->addColumn('updated_at', 'timestamp', [
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
'update' => 'CURRENT_TIMESTAMP',
|
||||
])
|
||||
->addIndex(['opt_key'], ['unique' => true])
|
||||
->create();
|
||||
|
||||
// Righe iniziali del popup
|
||||
$this->table('options')->insert([
|
||||
[
|
||||
'opt_key' => 'popup_enabled',
|
||||
'opt_value' => '1',
|
||||
],
|
||||
[
|
||||
'opt_key' => 'popup_version',
|
||||
'opt_value' => '2026-07-migrazione',
|
||||
],
|
||||
[
|
||||
'opt_key' => 'popup',
|
||||
'opt_value' =>
|
||||
'<h2 style="margin-top:0;">Nuovo portale disponibile</h2>'
|
||||
. '<p>Ci siamo spostati. Da ora usa il nuovo indirizzo:</p>'
|
||||
. '<p style="text-align:center;">'
|
||||
. '<a href="https://trfsmart-bv.cesoft.io/" '
|
||||
. 'style="display:inline-block;padding:12px 24px;background:#2563eb;'
|
||||
. 'color:#fff;text-decoration:none;border-radius:6px;">Vai al nuovo portale</a>'
|
||||
. '</p>'
|
||||
. '<p style="font-size:13px;color:#666;">Salva il nuovo indirizzo nei preferiti. '
|
||||
. 'Se riscontri problemi puoi continuare a usare questo portale per ora.</p>',
|
||||
],
|
||||
])->saveData();
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddExportDateToDatadb extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Aggiunge export_date su datadb: data/ora di esportazione verso il LIMS,
|
||||
* valorizzata insieme al numero commessa quando status passa a 'l'.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$this->table('datadb')
|
||||
->addColumn('export_date', 'datetime', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'commessaweb',
|
||||
'comment' => 'Data/ora esportazione verso LIMS (valorizzata quando status = l)',
|
||||
])
|
||||
->addIndex(['export_date'], ['name' => 'idx_datadb_export_date'])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreateLimsDailySamplesTable extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Tabella con un dato al giorno: numero totale di campioni inseriti
|
||||
* nel LIMS (loro software) in quella giornata. Serve per calcolare
|
||||
* la % di campioni esportati sul totale inserito nel LIMS.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('lims_daily_samples', [
|
||||
'id' => false,
|
||||
'primary_key' => ['id'],
|
||||
'comment' => 'Totale campioni inseriti nel LIMS per giornata',
|
||||
]);
|
||||
|
||||
$table
|
||||
->addColumn('id', 'integer', [
|
||||
'identity' => true,
|
||||
'signed' => false,
|
||||
])
|
||||
->addColumn('sample_date', 'date', [
|
||||
'null' => false,
|
||||
'comment' => 'Giornata di riferimento',
|
||||
])
|
||||
->addColumn('total_samples', 'integer', [
|
||||
'signed' => false,
|
||||
'null' => false,
|
||||
'default' => 0,
|
||||
'comment' => 'Totale campioni inseriti nel LIMS in quella giornata',
|
||||
])
|
||||
->addColumn('note', 'string', [
|
||||
'limit' => 255,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
])
|
||||
->addColumn('created_at', 'timestamp', [
|
||||
'null' => true,
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
])
|
||||
->addColumn('updated_at', 'timestamp', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'update' => 'CURRENT_TIMESTAMP',
|
||||
])
|
||||
->addIndex(['sample_date'], [
|
||||
'unique' => true,
|
||||
'name' => 'uq_lims_daily_samples_date',
|
||||
])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddTemplateNoteToExcelTemplates extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$this->table('excel_templates')
|
||||
->addColumn('template_note', 'text', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'idroutine',
|
||||
'comment' => 'Nota opzionale del template, aggiunta in fondo alle note in esportazione',
|
||||
])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class DropUniqPartAnalysisIndex extends AbstractMigration
|
||||
{
|
||||
public function up(): void
|
||||
{
|
||||
$table = $this->table('identification_parts_analyses');
|
||||
|
||||
if ($table->hasIndexByName('uniq_part_analysis')) {
|
||||
$table->removeIndexByName('uniq_part_analysis')->update();
|
||||
}
|
||||
}
|
||||
|
||||
public function down(): void
|
||||
{
|
||||
$table = $this->table('identification_parts_analyses');
|
||||
|
||||
if (!$table->hasIndexByName('uniq_part_analysis')) {
|
||||
$table->addIndex(['part_id', 'analysis_recordkey'], [
|
||||
'unique' => true,
|
||||
'name' => 'uniq_part_analysis',
|
||||
])->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +0,0 @@
|
||||
# DB LOCALE (Windows 11)
|
||||
url=jdbc:mysql://localhost:3306/trfcertest
|
||||
username=solocla
|
||||
password=!Massarosa2
|
||||
driver=com.mysql.cj.jdbc.Driver
|
||||
changeLogFile=liquibase/changelog/db.changelog-master.yaml
|
||||
@@ -1,27 +0,0 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||
$dotenv->safeLoad();
|
||||
|
||||
return [
|
||||
'paths' => [
|
||||
'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
|
||||
'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds',
|
||||
],
|
||||
'environments' => [
|
||||
'default_migration_table' => 'phinxlog',
|
||||
'default_environment' => 'production',
|
||||
'production' => [
|
||||
'adapter' => 'mysql',
|
||||
'host' => $_ENV['DB_HOST'] ?? 'localhost',
|
||||
'name' => $_ENV['DB_DATABASE'] ?? '',
|
||||
'user' => $_ENV['DB_USERNAME'] ?? '',
|
||||
'pass' => $_ENV['DB_PASSWORD'] ?? '',
|
||||
'port' => $_ENV['DB_PORT'] ?? 3306,
|
||||
'charset' => 'utf8mb4',
|
||||
],
|
||||
],
|
||||
'version_order' => 'creation',
|
||||
];
|
||||
@@ -32,4 +32,3 @@ $langdatatables = [
|
||||
"paginate_next" => "Next",
|
||||
"paginate_previous" => "Previous"
|
||||
];
|
||||
$quotationstitle = "Quotations";
|
||||
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php'); // Auth + $iduserlogin + DBHandlerSelect
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
throw new Exception('Invalid request method');
|
||||
}
|
||||
|
||||
$iddatadb = isset($_POST['iddatadb']) ? (int)$_POST['iddatadb'] : 0;
|
||||
|
||||
if ($iddatadb <= 0) {
|
||||
throw new Exception('Missing iddatadb');
|
||||
}
|
||||
|
||||
// ✅ Supporta sia singola stringa (con |) che array part_descriptions[]
|
||||
$parts = [];
|
||||
|
||||
if (isset($_POST['part_descriptions']) && is_array($_POST['part_descriptions'])) {
|
||||
$parts = $_POST['part_descriptions'];
|
||||
} else {
|
||||
$raw = isset($_POST['part_description']) ? (string)$_POST['part_description'] : '';
|
||||
// split su "|" per multi-part
|
||||
$parts = preg_split('/\s*\|\s*/', $raw);
|
||||
}
|
||||
|
||||
// normalizza: trim + rimuovi vuoti + dedup
|
||||
$cleanParts = [];
|
||||
foreach ($parts as $p) {
|
||||
$p = trim((string)$p);
|
||||
if ($p === '') continue;
|
||||
|
||||
// limita lunghezza come da DB varchar(255)
|
||||
if (mb_strlen($p) > 255) {
|
||||
$p = mb_substr($p, 0, 255);
|
||||
}
|
||||
$cleanParts[] = $p;
|
||||
}
|
||||
$cleanParts = array_values(array_unique($cleanParts));
|
||||
|
||||
if (count($cleanParts) === 0) {
|
||||
throw new Exception('Part description is empty');
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
try {
|
||||
// prende il prossimo part_number per iddatadb
|
||||
$stmtMax = $pdo->prepare("SELECT COALESCE(MAX(part_number), 0) AS maxnum FROM identification_parts WHERE iddatadb = ?");
|
||||
$stmtMax->execute([$iddatadb]);
|
||||
$nextNumber = (int)$stmtMax->fetchColumn() + 1;
|
||||
|
||||
$stmtIns = $pdo->prepare("
|
||||
INSERT INTO identification_parts (iddatadb, part_number, part_description)
|
||||
VALUES (?, ?, ?)
|
||||
");
|
||||
|
||||
$insertedIds = [];
|
||||
|
||||
foreach ($cleanParts as $p) {
|
||||
$ok = $stmtIns->execute([$iddatadb, $nextNumber, $p]);
|
||||
if (!$ok) {
|
||||
throw new Exception('Insert failed');
|
||||
}
|
||||
$insertedIds[] = $pdo->lastInsertId();
|
||||
$nextNumber++;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Parts added',
|
||||
'count' => count($insertedIds),
|
||||
'ids' => $insertedIds
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
throw $e;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
@@ -1,73 +0,0 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$templateId = intval($input['template_id'] ?? 0);
|
||||
$importRefFromClient = trim($input['importreferencecode'] ?? '');
|
||||
|
||||
if (!$templateId) {
|
||||
throw new Exception('Template ID missing');
|
||||
}
|
||||
|
||||
$userId = $iduserlogin ?? 1;
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// Get default idclient from template
|
||||
$stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
|
||||
$stmt->execute([$templateId]);
|
||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$idclient = $template['idclient'] ?? null;
|
||||
|
||||
// Use provided importreferencecode (from filtered page) or generate new
|
||||
$importReferenceCode = $importRefFromClient !== '' ? $importRefFromClient : date('YmdHis') . '-' . uniqid();
|
||||
|
||||
// Insert empty record
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO datadb (templateid, user_id, status, idclient, importreferencecode, importdate)
|
||||
VALUES (?, ?, 'i', ?, ?, NOW())
|
||||
");
|
||||
$stmt->execute([$templateId, $userId, $idclient, $importReferenceCode]);
|
||||
$iddatadb = (int)$pdo->lastInsertId();
|
||||
|
||||
// Create import_data_details for all mappings (with auto_value support)
|
||||
$mappingStmt = $pdo->prepare("SELECT id, auto_value, manual_default, data_type FROM template_mapping WHERE template_id = ?");
|
||||
$mappingStmt->execute([$templateId]);
|
||||
$mappings = $mappingStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!empty($mappings)) {
|
||||
$insertStmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
||||
foreach ($mappings as $m) {
|
||||
$val = '';
|
||||
$auto = $m['auto_value'] ?? 'none';
|
||||
if ($auto === 'import_date') {
|
||||
$val = date('Y-m-d');
|
||||
} elseif ($auto === 'import_time') {
|
||||
$val = date('H:i');
|
||||
} elseif ($m['data_type'] === 'DATE' && ($m['manual_default'] ?? '') === 'today') {
|
||||
$val = date('Y-m-d');
|
||||
} elseif (!empty($m['manual_default'])) {
|
||||
$val = $m['manual_default'];
|
||||
}
|
||||
$insertStmt->execute([$iddatadb, $m['id'], $val]);
|
||||
}
|
||||
}
|
||||
|
||||
// Get user name
|
||||
$userStmt = $pdo->prepare("SELECT CONCAT(first_name, ' ', last_name) AS user_name FROM auth_users WHERE id = ?");
|
||||
$userStmt->execute([$userId]);
|
||||
$userName = $userStmt->fetchColumn() ?: '';
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'iddatadb' => $iddatadb,
|
||||
'importreferencecode' => $importReferenceCode,
|
||||
'user_name' => $userName,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,878 +0,0 @@
|
||||
(function () {
|
||||
"use strict";
|
||||
let analysisMatriciMap = {};
|
||||
let analysisLoadedCache = {};
|
||||
let analysisAssignedState = {};
|
||||
let analysisSelectedState = {};
|
||||
|
||||
function loadAnalysisMatrixNames() {
|
||||
return $.ajax({
|
||||
url: "search_matrici.php?full_list=1",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
})
|
||||
.done(function (data) {
|
||||
analysisMatriciMap = {};
|
||||
|
||||
(data.value || []).forEach(function (matrice) {
|
||||
analysisMatriciMap[String(matrice.IdMatrice)] =
|
||||
matrice.NomeMatrice || "#" + matrice.IdMatrice;
|
||||
});
|
||||
|
||||
applyAnalysisMatrixNames();
|
||||
})
|
||||
.fail(function () {
|
||||
analysisMatriciMap = {};
|
||||
applyAnalysisMatrixNames();
|
||||
});
|
||||
}
|
||||
|
||||
function applyAnalysisMatrixNames() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
|
||||
const matrixId = item.getAttribute("data-matrix-id");
|
||||
const nameEl = item.querySelector(".analysis-matrix-name");
|
||||
|
||||
let matrixName = "No Matrix";
|
||||
if (matrixId && matrixId !== "NO_MATRIX") {
|
||||
matrixName =
|
||||
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||
}
|
||||
|
||||
item.setAttribute("data-matrix-name", matrixName);
|
||||
|
||||
if (nameEl) {
|
||||
nameEl.textContent = matrixName;
|
||||
}
|
||||
});
|
||||
|
||||
modal.querySelectorAll(".analysis-part-matrix-name").forEach((el) => {
|
||||
const matrixId = el.getAttribute("data-matrix-id");
|
||||
|
||||
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||
el.textContent = "No Matrix";
|
||||
return;
|
||||
}
|
||||
|
||||
el.textContent =
|
||||
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
|
||||
});
|
||||
}
|
||||
function readInitialAssignedAnalyses() {
|
||||
const jsonEl = document.getElementById("analysisAssignedInitialData");
|
||||
if (!jsonEl) {
|
||||
analysisAssignedState = {};
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
analysisAssignedState =
|
||||
JSON.parse(jsonEl.textContent || "{}") || {};
|
||||
} catch (e) {
|
||||
analysisAssignedState = {};
|
||||
}
|
||||
}
|
||||
|
||||
function getSelectedPartIds() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return [];
|
||||
|
||||
return Array.from(
|
||||
modal.querySelectorAll(".analysis-part-checkbox:checked"),
|
||||
).map((el) => String(el.value));
|
||||
}
|
||||
|
||||
function getCurrentSelectedAnalysisRecordKeys() {
|
||||
return Object.keys(analysisSelectedState).filter(function (key) {
|
||||
return analysisSelectedState[key] === true;
|
||||
});
|
||||
}
|
||||
|
||||
function renderAssignedAnalysesForPart(partId) {
|
||||
const container = document.getElementById(
|
||||
"analysisAssignedListPart" + partId,
|
||||
);
|
||||
if (!container) return;
|
||||
|
||||
const items = Array.isArray(analysisAssignedState[String(partId)])
|
||||
? analysisAssignedState[String(partId)]
|
||||
: [];
|
||||
|
||||
if (!items.length) {
|
||||
container.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
|
||||
container.innerHTML = items
|
||||
.map(function (item, index) {
|
||||
const recordKey = item.analysis_recordkey || "";
|
||||
const rowId = item.id != null ? item.id : "";
|
||||
const title = item.analysis_name || "Unnamed analysis";
|
||||
const method = item.analysis_method || "";
|
||||
|
||||
return `
|
||||
<div class="analysis-assigned-chip" data-recordkey="${escapeHtml(recordKey)}">
|
||||
<div class="analysis-assigned-chip-text">
|
||||
<div class="analysis-assigned-chip-title">${escapeHtml(title)}</div>
|
||||
${method ? `<div class="analysis-assigned-chip-method">${escapeHtml(method)}</div>` : ""}
|
||||
</div>
|
||||
<button type="button"
|
||||
class="analysis-remove-btn"
|
||||
data-part-id="${escapeHtml(partId)}"
|
||||
data-recordkey="${escapeHtml(recordKey)}"
|
||||
data-row-id="${escapeHtml(rowId)}"
|
||||
data-index="${index}"
|
||||
title="Remove analysis">×</button>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
}
|
||||
|
||||
function renderAssignedAnalysesForSelectedParts() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
modal.querySelectorAll(".analysis-part-item").forEach(function (item) {
|
||||
const partId = item.getAttribute("data-part-id");
|
||||
renderAssignedAnalysesForPart(partId);
|
||||
});
|
||||
}
|
||||
|
||||
function syncSelectedAnalysisRows() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
modal
|
||||
.querySelectorAll(".analysis-analysis-item")
|
||||
.forEach(function (item) {
|
||||
const recordKey = item.getAttribute("data-recordkey") || "";
|
||||
if (recordKey && analysisSelectedState[recordKey]) {
|
||||
item.classList.add("analysis-selected");
|
||||
} else {
|
||||
item.classList.remove("analysis-selected");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function buildAnalysisPayloadFromRow(rowEl) {
|
||||
return {
|
||||
analysis_recordkey: rowEl.getAttribute("data-recordkey") || "",
|
||||
analysis_name: rowEl.getAttribute("data-analysis-name") || "",
|
||||
analysis_method: rowEl.getAttribute("data-analysis-method") || "",
|
||||
analysis_level: rowEl.getAttribute("data-analysis-level") || "",
|
||||
is_web_selectable: rowEl.getAttribute("data-web") === "1" ? 1 : 0,
|
||||
is_accredited:
|
||||
rowEl.getAttribute("data-accredited") === "1" ? 1 : 0,
|
||||
};
|
||||
}
|
||||
|
||||
function addAnalysisToLocalState(
|
||||
partId,
|
||||
payload,
|
||||
iddatadb,
|
||||
idmatrice,
|
||||
newId,
|
||||
) {
|
||||
const key = String(partId);
|
||||
if (!Array.isArray(analysisAssignedState[key])) {
|
||||
analysisAssignedState[key] = [];
|
||||
}
|
||||
|
||||
{
|
||||
analysisAssignedState[key].push({
|
||||
id: newId || null,
|
||||
part_id: parseInt(partId, 10),
|
||||
iddatadb: iddatadb || null,
|
||||
idmatrice: idmatrice || null,
|
||||
analysis_recordkey: payload.analysis_recordkey,
|
||||
analysis_name: payload.analysis_name,
|
||||
analysis_method: payload.analysis_method,
|
||||
analysis_level:
|
||||
payload.analysis_level !== ""
|
||||
? parseInt(payload.analysis_level, 10)
|
||||
: null,
|
||||
is_web_selectable: payload.is_web_selectable,
|
||||
is_accredited: payload.is_accredited,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function removeAnalysisFromLocalState(partId, recordKey, index) {
|
||||
const key = String(partId);
|
||||
if (!Array.isArray(analysisAssignedState[key])) {
|
||||
return;
|
||||
}
|
||||
|
||||
const i = parseInt(index, 10);
|
||||
if (!isNaN(i) && i >= 0 && i < analysisAssignedState[key].length) {
|
||||
analysisAssignedState[key].splice(i, 1);
|
||||
return;
|
||||
}
|
||||
|
||||
const found = analysisAssignedState[key].findIndex(function (item) {
|
||||
return item.analysis_recordkey === recordKey;
|
||||
});
|
||||
if (found !== -1) {
|
||||
analysisAssignedState[key].splice(found, 1);
|
||||
}
|
||||
}
|
||||
|
||||
function saveAnalysisAssociation(partId, payload, callback) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const iddatadb =
|
||||
modal.querySelector("#analysisModalIddatadb")?.value || "";
|
||||
const partItem = modal.querySelector(
|
||||
'.analysis-part-item[data-part-id="' + partId + '"]',
|
||||
);
|
||||
const idmatrice = partItem
|
||||
? partItem.getAttribute("data-matrix-id") || ""
|
||||
: "";
|
||||
|
||||
$.ajax({
|
||||
url: "save_part_analysis.php",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
part_id: partId,
|
||||
iddatadb: iddatadb,
|
||||
idmatrice: idmatrice !== "NO_MATRIX" ? idmatrice : "",
|
||||
analysis_recordkey: payload.analysis_recordkey,
|
||||
analysis_name: payload.analysis_name,
|
||||
analysis_method: payload.analysis_method,
|
||||
analysis_level: payload.analysis_level,
|
||||
is_web_selectable: payload.is_web_selectable,
|
||||
is_accredited: payload.is_accredited,
|
||||
},
|
||||
})
|
||||
.done(function (resp) {
|
||||
addAnalysisToLocalState(
|
||||
partId,
|
||||
payload,
|
||||
iddatadb,
|
||||
idmatrice !== "NO_MATRIX" ? idmatrice : null,
|
||||
resp && resp.id ? resp.id : null,
|
||||
);
|
||||
renderAssignedAnalysesForPart(partId);
|
||||
if (typeof callback === "function") callback(true);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
let message = "Error saving analysis association";
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||
message = xhr.responseJSON.message;
|
||||
}
|
||||
alert(message);
|
||||
if (typeof callback === "function") callback(false);
|
||||
});
|
||||
}
|
||||
|
||||
function deleteAnalysisAssociation(
|
||||
partId,
|
||||
recordKey,
|
||||
rowId,
|
||||
index,
|
||||
callback,
|
||||
) {
|
||||
$.ajax({
|
||||
url: "delete_part_analysis.php",
|
||||
method: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
part_id: partId,
|
||||
analysis_recordkey: recordKey,
|
||||
row_id: rowId || "",
|
||||
},
|
||||
})
|
||||
.done(function () {
|
||||
removeAnalysisFromLocalState(partId, recordKey, index);
|
||||
renderAssignedAnalysesForPart(partId);
|
||||
if (typeof callback === "function") callback(true);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
let message = "Error deleting analysis association";
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
|
||||
message = xhr.responseJSON.message;
|
||||
}
|
||||
alert(message);
|
||||
if (typeof callback === "function") callback(false);
|
||||
});
|
||||
}
|
||||
function setAnalysisLoadingState(isLoading) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const loadingEl = modal.querySelector("#analysisLoadingBox");
|
||||
if (!loadingEl) return;
|
||||
|
||||
if (isLoading) {
|
||||
loadingEl.classList.remove("d-none");
|
||||
} else {
|
||||
loadingEl.classList.add("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function showAnalysisError(message) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
if (emptyEl) emptyEl.classList.add("d-none");
|
||||
|
||||
if (errorEl) {
|
||||
errorEl.textContent = message || "Error loading analyses";
|
||||
errorEl.classList.remove("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function showAnalysisEmpty(message) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
if (errorEl) errorEl.classList.add("d-none");
|
||||
|
||||
if (emptyEl) {
|
||||
emptyEl.textContent =
|
||||
message || "No analyses found for this matrix";
|
||||
emptyEl.classList.remove("d-none");
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
|
||||
function renderAnalysesList(analyses) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
|
||||
if (!listEl) return;
|
||||
|
||||
if (errorEl) errorEl.classList.add("d-none");
|
||||
|
||||
if (!Array.isArray(analyses) || analyses.length === 0) {
|
||||
showAnalysisEmpty("No analyses found for this matrix");
|
||||
return;
|
||||
}
|
||||
|
||||
if (emptyEl) emptyEl.classList.add("d-none");
|
||||
|
||||
listEl.innerHTML = analyses
|
||||
.map(function (item) {
|
||||
const recordKey = item.RecordKey || "";
|
||||
const analysisName =
|
||||
item.NomeAnalisiTraduzione ||
|
||||
item.NomeAnalisi ||
|
||||
"Unnamed analysis";
|
||||
const methodName = item.MetodoNome || "";
|
||||
const selectable = item.SelezionabileSuWeb === true;
|
||||
const accredited = item.Accreditato === true;
|
||||
const level = item.Livello ?? "";
|
||||
const searchText = (
|
||||
analysisName +
|
||||
" " +
|
||||
methodName
|
||||
).toLowerCase();
|
||||
|
||||
let badges = "";
|
||||
if (selectable) {
|
||||
badges += '<span class="badge bg-success">Web</span>';
|
||||
} else {
|
||||
badges += '<span class="badge bg-secondary">Not web</span>';
|
||||
}
|
||||
|
||||
if (accredited) {
|
||||
badges +=
|
||||
'<span class="badge bg-info text-dark">Accredited</span>';
|
||||
}
|
||||
|
||||
if (level !== "") {
|
||||
badges +=
|
||||
'<span class="badge bg-light text-dark border">Level ' +
|
||||
escapeHtml(level) +
|
||||
"</span>";
|
||||
}
|
||||
|
||||
return `
|
||||
<div
|
||||
class="list-group-item analysis-analysis-item"
|
||||
data-recordkey="${escapeHtml(recordKey)}"
|
||||
data-analysis-name="${escapeHtml(analysisName)}"
|
||||
data-analysis-method="${escapeHtml(methodName)}"
|
||||
data-analysis-level="${escapeHtml(level)}"
|
||||
data-web="${selectable ? "1" : "0"}"
|
||||
data-accredited="${accredited ? "1" : "0"}"
|
||||
data-search="${escapeHtml(searchText)}"
|
||||
>
|
||||
<div class="fw-semibold">${escapeHtml(analysisName)}</div>
|
||||
|
||||
${methodName ? `<div class="analysis-analysis-meta mt-1">${escapeHtml(methodName)}</div>` : ""}
|
||||
|
||||
<div class="analysis-analysis-badges mt-2">
|
||||
${badges}
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
})
|
||||
.join("");
|
||||
|
||||
filterAnalysisList();
|
||||
syncSelectedAnalysisRows();
|
||||
}
|
||||
|
||||
function filterAnalysisList() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
const items = modal.querySelectorAll(".analysis-analysis-item");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
|
||||
const webOnly = true;
|
||||
const searchValue = searchEl ? searchEl.value.trim().toLowerCase() : "";
|
||||
|
||||
let visibleCount = 0;
|
||||
|
||||
items.forEach((item) => {
|
||||
const isWeb = item.getAttribute("data-web") === "1";
|
||||
const searchText = (
|
||||
item.getAttribute("data-search") || ""
|
||||
).toLowerCase();
|
||||
|
||||
let visible = true;
|
||||
|
||||
if (webOnly && !isWeb) {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
if (visible && searchValue && !searchText.includes(searchValue)) {
|
||||
visible = false;
|
||||
}
|
||||
|
||||
item.style.display = visible ? "" : "none";
|
||||
|
||||
if (visible) {
|
||||
visibleCount++;
|
||||
}
|
||||
});
|
||||
|
||||
if (errorEl) {
|
||||
errorEl.classList.add("d-none");
|
||||
}
|
||||
|
||||
if (emptyEl) {
|
||||
if (items.length === 0) {
|
||||
emptyEl.textContent = "No analyses found for this matrix";
|
||||
emptyEl.classList.remove("d-none");
|
||||
} else if (visibleCount === 0) {
|
||||
emptyEl.textContent = "No analyses match the current filters";
|
||||
emptyEl.classList.remove("d-none");
|
||||
} else {
|
||||
emptyEl.classList.add("d-none");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadAnalysesByMatrix(matrixId) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
if (!matrixId || matrixId === "NO_MATRIX") {
|
||||
showAnalysisEmpty("No matrix selected");
|
||||
return;
|
||||
}
|
||||
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
const errorEl = modal.querySelector("#analysisErrorBox");
|
||||
const emptyEl = modal.querySelector("#analysisEmptyBox");
|
||||
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
if (errorEl) errorEl.classList.add("d-none");
|
||||
if (emptyEl) {
|
||||
emptyEl.textContent = "";
|
||||
emptyEl.classList.add("d-none");
|
||||
}
|
||||
|
||||
const cacheKey = String(matrixId) + "_WEB_ONLY";
|
||||
|
||||
if (analysisLoadedCache[cacheKey]) {
|
||||
renderAnalysesList(analysisLoadedCache[cacheKey]);
|
||||
return;
|
||||
}
|
||||
|
||||
setAnalysisLoadingState(true);
|
||||
|
||||
$.ajax({
|
||||
url: "get_analisi_matrice_filter.php",
|
||||
method: "GET",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id_matrice: matrixId,
|
||||
web_only: 1,
|
||||
},
|
||||
})
|
||||
.done(function (response) {
|
||||
const analyses = Array.isArray(response.value)
|
||||
? response.value.filter(function (item) {
|
||||
const name =
|
||||
item.NomeAnalisiTraduzione ||
|
||||
item.NomeAnalisi ||
|
||||
"";
|
||||
if (name.trim().startsWith("*")) {
|
||||
return false;
|
||||
}
|
||||
return (
|
||||
item.SelezionabileSuWeb === true ||
|
||||
item.SelezionabileSuWeb === 1 ||
|
||||
item.SelezionabileSuWeb === "1"
|
||||
);
|
||||
})
|
||||
: [];
|
||||
|
||||
analysisLoadedCache[cacheKey] = analyses;
|
||||
renderAnalysesList(analyses);
|
||||
})
|
||||
.fail(function (xhr) {
|
||||
let message = "Error loading analyses";
|
||||
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
|
||||
message = xhr.responseJSON.error;
|
||||
}
|
||||
showAnalysisError(message);
|
||||
})
|
||||
.always(function () {
|
||||
setAnalysisLoadingState(false);
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelectedPartsInfo() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const checked = modal.querySelectorAll(
|
||||
".analysis-part-checkbox:checked",
|
||||
);
|
||||
const ids = Array.from(checked).map((el) => el.value);
|
||||
|
||||
const countEl = modal.querySelector("#analysisSelectedPartsCount");
|
||||
const idsEl = modal.querySelector("#analysisSelectedPartsIds");
|
||||
|
||||
if (countEl) {
|
||||
countEl.textContent = `${ids.length} selected`;
|
||||
}
|
||||
|
||||
if (idsEl) {
|
||||
idsEl.textContent = ids.length ? ids.join(", ") : "-";
|
||||
}
|
||||
|
||||
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
|
||||
const checkbox = item.querySelector(".analysis-part-checkbox");
|
||||
if (checkbox && checkbox.checked) {
|
||||
item.classList.add("part-checked");
|
||||
} else {
|
||||
item.classList.remove("part-checked");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function selectPartsByMatrix(matrixId, matrixLabel) {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
const partItems = modal.querySelectorAll(".analysis-part-item");
|
||||
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||
|
||||
partItems.forEach((item) => {
|
||||
const itemMatrixId = item.getAttribute("data-matrix-id");
|
||||
const checkbox = item.querySelector(".analysis-part-checkbox");
|
||||
|
||||
item.classList.remove("matrix-active");
|
||||
|
||||
if (itemMatrixId === String(matrixId)) {
|
||||
item.classList.add("matrix-active");
|
||||
if (checkbox) checkbox.checked = true;
|
||||
} else {
|
||||
if (checkbox) checkbox.checked = false;
|
||||
}
|
||||
});
|
||||
|
||||
if (matrixLabelEl) {
|
||||
matrixLabelEl.textContent = matrixLabel || "-";
|
||||
}
|
||||
analysisSelectedState = {};
|
||||
|
||||
const selectedPartIds = getSelectedPartIds();
|
||||
selectedPartIds.forEach(function (partId) {
|
||||
const assigned = Array.isArray(
|
||||
analysisAssignedState[String(partId)],
|
||||
)
|
||||
? analysisAssignedState[String(partId)]
|
||||
: [];
|
||||
|
||||
assigned.forEach(function (item) {
|
||||
if (item.analysis_recordkey) {
|
||||
analysisSelectedState[item.analysis_recordkey] = true;
|
||||
}
|
||||
});
|
||||
});
|
||||
updateSelectedPartsInfo();
|
||||
loadAnalysesByMatrix(matrixId);
|
||||
}
|
||||
|
||||
function clearAnalysisSelection() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
|
||||
item.classList.remove("active");
|
||||
});
|
||||
|
||||
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
|
||||
item.classList.remove("matrix-active", "part-checked");
|
||||
});
|
||||
|
||||
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
|
||||
cb.checked = false;
|
||||
});
|
||||
|
||||
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
|
||||
if (matrixLabelEl) matrixLabelEl.textContent = "-";
|
||||
|
||||
const webOnlyEl = modal.querySelector("#analysisWebOnly");
|
||||
if (webOnlyEl) webOnlyEl.checked = false;
|
||||
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
if (searchEl) searchEl.value = "";
|
||||
|
||||
const listEl = modal.querySelector("#analysisList");
|
||||
if (listEl) listEl.innerHTML = "";
|
||||
|
||||
showAnalysisEmpty("Select a matrix to load analyses");
|
||||
updateSelectedPartsInfo();
|
||||
}
|
||||
|
||||
function initAnalysisModal() {
|
||||
const modal = document.getElementById("analysisModal");
|
||||
if (!modal) return;
|
||||
|
||||
analysisLoadedCache = {};
|
||||
analysisSelectedState = {};
|
||||
readInitialAssignedAnalyses();
|
||||
renderAssignedAnalysesForSelectedParts();
|
||||
|
||||
modal.querySelectorAll(".analysis-matrix-item").forEach((btn) => {
|
||||
btn.addEventListener("click", function () {
|
||||
modal
|
||||
.querySelectorAll(".analysis-matrix-item")
|
||||
.forEach((x) => x.classList.remove("active"));
|
||||
this.classList.add("active");
|
||||
|
||||
const matrixId = this.getAttribute("data-matrix-id");
|
||||
const matrixLabel =
|
||||
this.getAttribute("data-matrix-name") ||
|
||||
this.textContent.trim();
|
||||
|
||||
selectPartsByMatrix(matrixId, matrixLabel);
|
||||
});
|
||||
});
|
||||
|
||||
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
|
||||
cb.addEventListener("change", function () {
|
||||
updateSelectedPartsInfo();
|
||||
});
|
||||
});
|
||||
|
||||
const clearBtn = modal.querySelector("#analysisClearSelectionBtn");
|
||||
if (clearBtn) {
|
||||
clearBtn.addEventListener("click", function () {
|
||||
clearAnalysisSelection();
|
||||
});
|
||||
}
|
||||
|
||||
// WEB only is now fixed by default
|
||||
|
||||
const searchEl = modal.querySelector("#analysisSearchInput");
|
||||
if (searchEl) {
|
||||
searchEl.addEventListener("input", function () {
|
||||
filterAnalysisList();
|
||||
});
|
||||
}
|
||||
modal.addEventListener("click", function (e) {
|
||||
const removeBtn = e.target.closest(".analysis-remove-btn");
|
||||
if (removeBtn) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
const partId = removeBtn.getAttribute("data-part-id");
|
||||
const recordKey = removeBtn.getAttribute("data-recordkey");
|
||||
const rowId = removeBtn.getAttribute("data-row-id");
|
||||
const index = removeBtn.getAttribute("data-index");
|
||||
|
||||
deleteAnalysisAssociation(
|
||||
partId,
|
||||
recordKey,
|
||||
rowId,
|
||||
index,
|
||||
function () {
|
||||
const stillUsed = Object.keys(
|
||||
analysisAssignedState,
|
||||
).some(function (pid) {
|
||||
return (
|
||||
Array.isArray(analysisAssignedState[pid]) &&
|
||||
analysisAssignedState[pid].some(
|
||||
function (item) {
|
||||
return (
|
||||
item.analysis_recordkey ===
|
||||
recordKey
|
||||
);
|
||||
},
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
if (!stillUsed) {
|
||||
delete analysisSelectedState[recordKey];
|
||||
}
|
||||
|
||||
syncSelectedAnalysisRows();
|
||||
},
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
const row = e.target.closest(".analysis-analysis-item");
|
||||
if (!row) return;
|
||||
|
||||
const selectedPartIds = getSelectedPartIds();
|
||||
if (!selectedPartIds.length) {
|
||||
alert("Select at least one part first");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload = buildAnalysisPayloadFromRow(row);
|
||||
if (!payload.analysis_recordkey) {
|
||||
alert("Invalid analysis record key");
|
||||
return;
|
||||
}
|
||||
|
||||
const recordKey = payload.analysis_recordkey;
|
||||
|
||||
let pending = selectedPartIds.length;
|
||||
let atLeastOneSaved = false;
|
||||
|
||||
selectedPartIds.forEach(function (partId) {
|
||||
saveAnalysisAssociation(partId, payload, function (ok) {
|
||||
if (ok) atLeastOneSaved = true;
|
||||
|
||||
pending--;
|
||||
if (pending <= 0 && atLeastOneSaved) {
|
||||
analysisSelectedState[recordKey] = true;
|
||||
syncSelectedAnalysisRows();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const firstActiveMatrix = modal.querySelector(
|
||||
".analysis-matrix-item.active",
|
||||
);
|
||||
if (firstActiveMatrix) {
|
||||
const matrixId = firstActiveMatrix.getAttribute("data-matrix-id");
|
||||
const matrixLabel =
|
||||
firstActiveMatrix.getAttribute("data-matrix-name") ||
|
||||
firstActiveMatrix.textContent.trim();
|
||||
|
||||
selectPartsByMatrix(matrixId, matrixLabel);
|
||||
} else {
|
||||
updateSelectedPartsInfo();
|
||||
}
|
||||
}
|
||||
|
||||
// OPEN ANALYSIS MODAL FROM PARTS MODAL BUTTON
|
||||
$(document).on("click", ".open-analysis-modal-btn", function () {
|
||||
const partsModal = document.getElementById("partsModal");
|
||||
const iddatadb = $("#partsModal").data("iddatadb");
|
||||
|
||||
if (!iddatadb) {
|
||||
console.error("iddatadb not found on #partsModal");
|
||||
alert("iddatadb not found");
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax({
|
||||
url: "modal_analysis.php",
|
||||
method: "GET",
|
||||
data: { iddatadb: iddatadb },
|
||||
success: function (response) {
|
||||
$("#analysisModalContainer").html(response);
|
||||
|
||||
const modalElement = document.getElementById("analysisModal");
|
||||
if (!modalElement) {
|
||||
console.error("Analysis modal not found: #analysisModal");
|
||||
return;
|
||||
}
|
||||
|
||||
let modal = bootstrap.Modal.getInstance(modalElement);
|
||||
if (!modal) {
|
||||
modal = new bootstrap.Modal(modalElement, {
|
||||
backdrop: true,
|
||||
keyboard: true,
|
||||
focus: true,
|
||||
});
|
||||
}
|
||||
|
||||
loadAnalysisMatrixNames().always(function () {
|
||||
initAnalysisModal();
|
||||
modal.show();
|
||||
});
|
||||
},
|
||||
error: function (xhr, status, error) {
|
||||
console.error("Error loading analysis modal:", error);
|
||||
alert("Error loading analysis modal: " + error);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
// CLEANUP ON CLOSE
|
||||
$(document).on("hidden.bs.modal", "#analysisModal", function () {
|
||||
const modalElement = document.getElementById("analysisModal");
|
||||
if (modalElement) {
|
||||
const modal = bootstrap.Modal.getInstance(modalElement);
|
||||
if (modal) modal.dispose();
|
||||
}
|
||||
|
||||
$("#analysisModalContainer").empty();
|
||||
|
||||
// keep parts modal alive, but remove extra backdrop leftovers
|
||||
$(".modal-backdrop").last().remove();
|
||||
|
||||
if ($("#partsModal").hasClass("show")) {
|
||||
$("body").addClass("modal-open");
|
||||
} else {
|
||||
$("body").removeClass("modal-open").css("padding-right", "");
|
||||
}
|
||||
});
|
||||
})();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
ob_start();
|
||||
session_start();
|
||||
require_once '../../vendor/autoload.php';
|
||||
|
||||
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
|
||||
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||
|
||||
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$template_id = isset($input['template_id']) ? intval($input['template_id']) : 0;
|
||||
$filename = $input['routine_data']['filename'] ?? '';
|
||||
$headerrow = $input['routine_data']['headerrow'] ?? 1;
|
||||
$excelData = $input['excel_data'] ?? [];
|
||||
$routineData = $input['routine_data'] ?? [];
|
||||
|
||||
if (!$filename || empty($excelData)) {
|
||||
throw new Exception("Dati della routine mancanti.");
|
||||
}
|
||||
|
||||
$routineFile = __DIR__ . '/routines/' . $filename;
|
||||
if (file_exists($routineFile)) {
|
||||
include_once $routineFile;
|
||||
$routineData['xls_headers'] = $_SESSION['headers'] ?? [];
|
||||
applyRoutine($excelData, $routineData); // Modifica $excelData in place
|
||||
error_log("Routine {$routineData['name']} applicata (file: {$filename}) per template {$template_id}, header row: {$headerrow}");
|
||||
} else {
|
||||
throw new Exception("File della routine non trovato: $routineFile");
|
||||
}
|
||||
|
||||
// Aggiorna la sessione con i dati modificati
|
||||
$_SESSION['excel_data'] = $excelData;
|
||||
|
||||
$response['excel_data'] = $excelData;
|
||||
$response['rows'] = array_column($excelData, 'data');
|
||||
$response['columns'] = $_SESSION['headers'];
|
||||
$response['template_id'] = $template_id;
|
||||
$response['filename'] = $input['filename'] ?? '';
|
||||
} else {
|
||||
$response['error'] = "Richiesta non valida.";
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$response['error'] = "Errore durante l'applicazione della routine: " . $e->getMessage();
|
||||
error_log("Exception in apply_routine.php: " . $e->getMessage());
|
||||
}
|
||||
|
||||
ob_end_clean();
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($response);
|
||||
exit;
|
||||
@@ -1,131 +0,0 @@
|
||||
<?php
|
||||
require_once "class/VisualLimsApiClient.class.php";
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
// 🔹 Configura directory log (creala se non esiste)
|
||||
$logDir = __DIR__ . '/logs/api/';
|
||||
if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
|
||||
// 🔹 Base URL API
|
||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
// 🔹 Hardcoded values
|
||||
$iddatadb = 846;
|
||||
$commessaId = 533357;
|
||||
|
||||
try {
|
||||
// 🔹 STEP 4: Fetch Field Values with Labels (usa dati reali per iddatadb=845)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
idd.field_value,
|
||||
m.field_label,
|
||||
m.schema_id,
|
||||
m.field_id
|
||||
FROM
|
||||
import_data_details as idd
|
||||
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||
WHERE idd.id = :iddatadb
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$fieldValues = [];
|
||||
$valueMap = []; // Mappa per field_id -> valore
|
||||
foreach ($rows as $row) {
|
||||
$fieldValues[] = [
|
||||
"IdCommesseCustomFields" => (int) $row['field_id'],
|
||||
"Valore" => $row['field_value'],
|
||||
"FieldLabel" => $row['field_label']
|
||||
];
|
||||
$valueMap[(int) $row['field_id']] = $row['field_value']; // Mappa per ID definizione
|
||||
}
|
||||
|
||||
// Logga i fieldValues in error_log
|
||||
$logFieldValues = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT);
|
||||
error_log($logFieldValues);
|
||||
|
||||
// 🔹 Initialize API client
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// 🔹 STEP A: GET iniziale per CommesseCustomFields con espansione CustomField
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)"; // Espansione come da istruzioni fornitore
|
||||
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
||||
|
||||
// 🔹 STEP B: Prepara payload PATCH (tutti i campi, sovrascrivi se match su CustomField.IdCustomField == field_id)
|
||||
$commessaCustomFields = [];
|
||||
foreach ($commessaWithFields["CommesseCustomFields"] as $customField) {
|
||||
$definitionId = (int) ($customField["CustomField"]["IdCustomField"] ?? 0); // Usa IdCustomField dal CustomField
|
||||
$currentValue = $customField["Valore"] ?? '';
|
||||
$newValue = isset($valueMap[$definitionId]) ? $valueMap[$definitionId] : $currentValue;
|
||||
|
||||
$commessaCustomFields[] = [
|
||||
"IdCommesseCustomFields" => (int) $customField["IdCommesseCustomFields"],
|
||||
"Valore" => $newValue
|
||||
];
|
||||
}
|
||||
|
||||
// 🔹 Unico file di log per tutto
|
||||
$logFile = $logDir . "commessa_{$commessaId}_patch_and_get_" . time() . ".txt";
|
||||
$logContent = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT) . "\n\n";
|
||||
|
||||
// Log curl-like per GET iniziale
|
||||
$logContent .= "GET iniziale:\n" .
|
||||
"curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaWithFields, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||
|
||||
if (!empty($commessaCustomFields)) {
|
||||
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
||||
|
||||
// Log curl-like per PATCH
|
||||
$jsonPayload = json_encode($updatePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$logContent .= "PATCH:\n" .
|
||||
"curl --location --request PATCH '{$apiBaseUrl}CommessaWeb({$commessaId})' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$jsonPayload}'\n\n";
|
||||
|
||||
$patchResponse = $api->patch("CommessaWeb({$commessaId})", $updatePayload);
|
||||
|
||||
$logContent .= "PATCH RESPONSE:\n" . json_encode($patchResponse, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||
} else {
|
||||
$logContent .= "PATCH: Nessun campo custom da aggiornare\n\n---\n";
|
||||
}
|
||||
|
||||
// 🔹 STEP C: GET di controllo post-PATCH
|
||||
$commessaAfterPatch = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
||||
|
||||
// Log curl-like per GET di controllo
|
||||
$logContent .= "GET di controllo:\n" .
|
||||
"curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||
|
||||
// Salva log unico
|
||||
file_put_contents($logFile, $logContent);
|
||||
|
||||
// 🔹 Output a schermo
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"message" => "PATCH eseguito su commessa {$commessaId} con dati da iddatadb {$iddatadb}",
|
||||
"commessaAfterPatch" => $commessaAfterPatch,
|
||||
"totalCustomFieldsUpdated" => count($commessaCustomFields),
|
||||
"fieldValues" => $fieldValues,
|
||||
"logFile" => $logFile
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
error_log("Patch Error: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
|
||||
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Patch failed: " . $e->getMessage(),
|
||||
"logFile" => $logFile ?? 'Nessun log generato'
|
||||
]);
|
||||
}
|
||||
@@ -24,22 +24,12 @@ class VisualLimsApiClient
|
||||
public static function getInstance()
|
||||
{
|
||||
if (self::$instance === null) {
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
|
||||
$dotenv->load();
|
||||
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
|
||||
if ($simulate) {
|
||||
require_once __DIR__ . '/VisualLimsApiClientMock.class.php';
|
||||
self::$instance = new VisualLimsApiClientMock();
|
||||
} else {
|
||||
self::$instance = new VisualLimsApiClient();
|
||||
}
|
||||
self::$instance = new VisualLimsApiClient();
|
||||
}
|
||||
return self::$instance;
|
||||
}
|
||||
|
||||
private function authenticate($retryCount = 0, $maxRetries = 3)
|
||||
private function authenticate()
|
||||
{
|
||||
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
@@ -55,22 +45,16 @@ class VisualLimsApiClient
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
||||
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'a') ?: fopen('php://stderr', 'w');
|
||||
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'w') ?: fopen('php://stderr', 'w');
|
||||
curl_setopt($ch, CURLOPT_STDERR, $log);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
$log_message = date('Y-m-d H:i:s') . " - Auth attempt {$retryCount}: HTTP {$http_code}, Error: {$curl_error}, Response: " . substr($response, 0, 1000) . "\n";
|
||||
fwrite($log, $log_message);
|
||||
fclose($log);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false || $http_code != 200) {
|
||||
if ($http_code === 400 && strpos($response, 'Cannot persist the object') !== false && $retryCount < $maxRetries) {
|
||||
usleep(500000); // Ritardo di 500ms
|
||||
return $this->authenticate($retryCount + 1, $maxRetries); // Riprova
|
||||
}
|
||||
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
|
||||
}
|
||||
|
||||
@@ -136,158 +120,4 @@ class VisualLimsApiClient
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function post($endpoint, $payload)
|
||||
{
|
||||
$token = $this->getToken();
|
||||
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Content-Type: application/json",
|
||||
"Accept: application/json"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore nella richiesta POST: {$curl_error}");
|
||||
}
|
||||
|
||||
if ($http_code < 200 || $http_code >= 300) {
|
||||
throw new Exception("POST fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
public function patch($endpoint, $payload)
|
||||
{
|
||||
$token = $this->getToken();
|
||||
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Content-Type: application/json",
|
||||
"Accept: application/json"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore nella richiesta PATCH: {$curl_error}");
|
||||
}
|
||||
|
||||
if ($http_code < 200 || $http_code >= 300) {
|
||||
throw new Exception("PATCH fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* POST a file as multipart/form-data (used for photo/attachment uploads).
|
||||
*
|
||||
* @param string $endpoint OData endpoint, e.g. "Campione(613388)/UploadCampioneFile"
|
||||
* @param string $filePath Absolute path to the file on disk
|
||||
* @param string $fileName Original file name to send
|
||||
* @param array $extraFields Additional form fields to include
|
||||
* @return array|null Decoded JSON response
|
||||
*/
|
||||
public function postMultipart($endpoint, $filePath, $fileName, array $extraFields = [])
|
||||
{
|
||||
$token = $this->getToken();
|
||||
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
|
||||
|
||||
$cfile = new CURLFile($filePath, mime_content_type($filePath) ?: 'application/octet-stream', $fileName);
|
||||
|
||||
$payload = array_merge($extraFields, [
|
||||
'file' => $cfile,
|
||||
]);
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/json",
|
||||
// Content-Type is set automatically to multipart/form-data by cURL
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore nella richiesta POST multipart: {$curl_error}");
|
||||
}
|
||||
|
||||
if ($http_code < 200 || $http_code >= 300) {
|
||||
throw new Exception("POST multipart fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
|
||||
}
|
||||
|
||||
return json_decode($response, true);
|
||||
}
|
||||
|
||||
public function getBaseUrl()
|
||||
{
|
||||
return $this->baseUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupera contenuto binario - Adattato per https://bvcpsitaly-elims.com/limsapi
|
||||
*/
|
||||
public function getRaw($endpoint)
|
||||
{
|
||||
$token = $this->getToken();
|
||||
|
||||
// IMPORTANTE: usa /odata/ e NON /api/odata/
|
||||
$url = "{$this->baseUrl}/odata/{$endpoint}";
|
||||
|
||||
$ch = curl_init($url);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: */*"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore cURL: " . $curl_error);
|
||||
}
|
||||
|
||||
if ($http_code !== 200) {
|
||||
throw new Exception("HTTP {$http_code} su endpoint: " . $url);
|
||||
}
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,135 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Mock implementation of VisualLimsApiClient.
|
||||
* Activated when SIMULATE_EXPORT_LIMS=true in .env.
|
||||
* All HTTP calls are skipped; fake but structurally valid data is returned.
|
||||
* Every simulated call is logged via error_log() with a [SIMULATE] prefix.
|
||||
*/
|
||||
class VisualLimsApiClientMock
|
||||
{
|
||||
private int $fakeCommessaId;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
// Stable fake ID for the lifetime of a single request
|
||||
$this->fakeCommessaId = mt_rand(90001, 99999);
|
||||
error_log("[SIMULATE] VisualLimsApiClientMock initialised (fakeCommessaId={$this->fakeCommessaId})");
|
||||
}
|
||||
|
||||
public function get(string $endpoint): array
|
||||
{
|
||||
error_log("[SIMULATE] GET {$endpoint}");
|
||||
|
||||
// --- Fixed-field dropdown lists ---
|
||||
|
||||
if (str_starts_with($endpoint, 'MoltiplicatorePrezzi')) {
|
||||
return ['value' => [
|
||||
['IdMoltiplicatorePrezzo' => 1, 'Codice' => 'MP-01', 'Descrizione' => 'Standard (1x)'],
|
||||
['IdMoltiplicatorePrezzo' => 2, 'Codice' => 'MP-02', 'Descrizione' => 'Urgente (1.5x)'],
|
||||
['IdMoltiplicatorePrezzo' => 3, 'Codice' => 'MP-03', 'Descrizione' => 'Extra Urgente (2x)'],
|
||||
]];
|
||||
}
|
||||
|
||||
if (str_starts_with($endpoint, 'AnagraficaCertestObject')) {
|
||||
return ['value' => [
|
||||
['IdAnagrafica' => 1, 'Codice' => 'OBJ-01', 'NomeAnagrafica' => 'Articolo Tessile'],
|
||||
['IdAnagrafica' => 2, 'Codice' => 'OBJ-02', 'NomeAnagrafica' => 'Componente Meccanico'],
|
||||
['IdAnagrafica' => 3, 'Codice' => 'OBJ-03', 'NomeAnagrafica' => 'Materiale Plastico'],
|
||||
]];
|
||||
}
|
||||
|
||||
if (str_starts_with($endpoint, 'AnagraficaCertestService')) {
|
||||
return ['value' => [
|
||||
['IdAnagrafica' => 1, 'Codice' => 'SRV-01', 'NomeAnagrafica' => 'Analisi Chimica'],
|
||||
['IdAnagrafica' => 2, 'Codice' => 'SRV-02', 'NomeAnagrafica' => 'Test Meccanico'],
|
||||
['IdAnagrafica' => 3, 'Codice' => 'SRV-03', 'NomeAnagrafica' => 'Prova Ambientale'],
|
||||
]];
|
||||
}
|
||||
|
||||
// Cliente? list — get_clienti.php exits early in simulate mode, but guard here too
|
||||
if (str_starts_with($endpoint, 'Cliente?')) {
|
||||
return ['value' => []];
|
||||
}
|
||||
|
||||
// Cliente(N)?$expand=Responsabili
|
||||
if (str_starts_with($endpoint, 'Cliente(')) {
|
||||
preg_match('/Cliente\((\d+)\)/', $endpoint, $m);
|
||||
$clienteId = isset($m[1]) ? (int) $m[1] : 0;
|
||||
return [
|
||||
'IdCliente' => $clienteId,
|
||||
'Responsabili' => [
|
||||
['IdClienteResponsabile' => 1, 'Nominativo' => 'Marco Bianchi'],
|
||||
['IdClienteResponsabile' => 2, 'Nominativo' => 'Giulia Ferrari'],
|
||||
['IdClienteResponsabile' => 3, 'Nominativo' => 'Andrea Russo'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- CustomField dropdown values (get_customfield_values.php) ---
|
||||
|
||||
if (str_starts_with($endpoint, 'CustomField(')) {
|
||||
preg_match('/CustomField\((\d+)\)/', $endpoint, $m);
|
||||
$fieldId = isset($m[1]) ? (int) $m[1] : 0;
|
||||
return [
|
||||
'CustomFieldsValues' => [
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 1, 'Valore' => 'Opzione A'],
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 2, 'Valore' => 'Opzione B'],
|
||||
['IdCustomFieldsValue' => $fieldId * 10 + 3, 'Valore' => 'Opzione C'],
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
// --- CommessaWeb OData calls (STEP 7 GET + STEP 10 verification) ---
|
||||
|
||||
preg_match('/\((\d+)\)/', $endpoint, $m);
|
||||
$id = isset($m[1]) ? (int) $m[1] : $this->fakeCommessaId;
|
||||
|
||||
return [
|
||||
'IdCommessa' => $id,
|
||||
'CodiceCommessa' => "SIM-{$id}",
|
||||
'CommesseCustomFields' => [], // Empty → PATCH step is skipped correctly
|
||||
];
|
||||
}
|
||||
|
||||
public function post(string $endpoint, array $payload): array
|
||||
{
|
||||
error_log("[SIMULATE] POST {$endpoint} payload=" . json_encode($payload));
|
||||
|
||||
// CommessaWeb creation
|
||||
if ($endpoint === 'CommessaWeb') {
|
||||
return [
|
||||
'IdCommessa' => $this->fakeCommessaId,
|
||||
'CodiceCommessa' => "SIM-{$this->fakeCommessaId}",
|
||||
'Richiedente' => $payload['Richiedente'] ?? '',
|
||||
'Descrizione' => $payload['Descrizione'] ?? '',
|
||||
];
|
||||
}
|
||||
|
||||
// Campione creation
|
||||
if ($endpoint === 'Campione') {
|
||||
return [
|
||||
'IdCampione' => mt_rand(10001, 19999),
|
||||
'Commessa' => $payload['Commessa'] ?? null,
|
||||
'Matrice' => $payload['Matrice'] ?? null,
|
||||
];
|
||||
}
|
||||
|
||||
// InviaCommessa / ImportaCommessa (currently commented out upstream)
|
||||
return ['simulated' => true, 'endpoint' => $endpoint];
|
||||
}
|
||||
|
||||
public function patch(string $endpoint, array $payload): array
|
||||
{
|
||||
error_log("[SIMULATE] PATCH {$endpoint} payload=" . json_encode($payload));
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function postMultipart(string $endpoint, string $filePath, string $fileName, array $extraFields = []): array
|
||||
{
|
||||
error_log("[SIMULATE] POST multipart {$endpoint} file={$fileName}");
|
||||
|
||||
return ['simulated' => true, 'file' => $fileName];
|
||||
}
|
||||
}
|
||||
@@ -3,9 +3,6 @@ require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
Dotenv::createImmutable(dirname(__DIR__, 3))->safeLoad();
|
||||
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||
|
||||
class DBHandlerSelect
|
||||
{
|
||||
private static $instance = null;
|
||||
|
||||
@@ -1,152 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* debug_odata_metadata.php — TEMPORANEO, da rimuovere dopo il test.
|
||||
* Scopo: leggere $metadata (XML standard OData) per vedere:
|
||||
* - tutti gli EntitySet disponibili (non solo "Cliente")
|
||||
* - tutti i campi (Property) definiti per l'EntityType Cliente
|
||||
* Questo bypassa qualunque vista/proiezione ristretta lato server
|
||||
* applicata sull'endpoint "Cliente" normale.
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
|
||||
$dotenv->load();
|
||||
|
||||
$baseUrl = $_ENV['API_BASE_URL'];
|
||||
$username = $_ENV['API_USERNAME'];
|
||||
$password = $_ENV['API_PASSWORD'];
|
||||
|
||||
$scriptStart = microtime(true);
|
||||
|
||||
// ── 1. Autenticazione (stessa logica della classe originale) ──────────────
|
||||
$ch = curl_init("{$baseUrl}/api/authentication/authenticate");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'Username' => $username,
|
||||
'Password' => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$authResponse = curl_exec($ch);
|
||||
$authHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($authHttpCode !== 200) {
|
||||
echo "AUTH FAILED: HTTP {$authHttpCode}\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$tokenData = json_decode($authResponse, true);
|
||||
$token = null;
|
||||
if (is_array($tokenData) && isset($tokenData['token'])) {
|
||||
$token = $tokenData['token'];
|
||||
} elseif (is_string($tokenData)) {
|
||||
$token = trim($tokenData, '"');
|
||||
} else {
|
||||
$token = trim($authResponse, '"');
|
||||
}
|
||||
|
||||
if (empty($token)) {
|
||||
echo "TOKEN NOT RECEIVED\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$authElapsed = microtime(true) - $scriptStart;
|
||||
|
||||
// ── 2. Richiesta $metadata (XML, non JSON) ─────────────────────────────────
|
||||
// Cache-buster nell'URL: alcuni server/reverse-proxy potrebbero cachare per URL esatto.
|
||||
$cacheBuster = uniqid('cb_', true);
|
||||
$metadataUrl = "{$baseUrl}/api/odata/\$metadata?_cb={$cacheBuster}";
|
||||
|
||||
$metadataStart = microtime(true);
|
||||
|
||||
$ch = curl_init($metadataUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/xml"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HEADER, true); // includi header risposta per ispezione
|
||||
|
||||
$rawResponse = curl_exec($ch);
|
||||
$curlInfo = curl_getinfo($ch);
|
||||
$httpCode = $curlInfo['http_code'];
|
||||
$curlTotalTime = $curlInfo['total_time'];
|
||||
$curlError = curl_error($ch);
|
||||
$headerSize = $curlInfo['header_size'];
|
||||
curl_close($ch);
|
||||
|
||||
$metadataElapsed = microtime(true) - $metadataStart;
|
||||
|
||||
if ($rawResponse === false || $httpCode !== 200) {
|
||||
echo "METADATA REQUEST FAILED: HTTP {$httpCode}\nError: {$curlError}\n{$rawResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$responseHeaders = substr($rawResponse, 0, $headerSize);
|
||||
$xml = substr($rawResponse, $headerSize);
|
||||
|
||||
echo "==================== TIMING (prova che NON è cache) ====================\n";
|
||||
echo "Tempo autenticazione: " . round($authElapsed * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (curl total_time): " . round($curlTotalTime * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (wall clock PHP): " . round($metadataElapsed * 1000, 1) . " ms\n";
|
||||
echo "Cache-buster usato: {$cacheBuster}\n";
|
||||
echo "Dimensione XML ricevuto: " . strlen($xml) . " bytes\n\n";
|
||||
|
||||
echo "==================== HEADER RISPOSTA (controlla se c'è Cache-Control/Age/X-Cache) ====================\n";
|
||||
echo $responseHeaders . "\n";
|
||||
|
||||
// ── 3. Estrai tutti gli EntitySet disponibili ──────────────────────────────
|
||||
echo "==================== ENTITY SETS DISPONIBILI ====================\n";
|
||||
if (preg_match_all('/<EntitySet Name="([^"]+)"/', $xml, $matches)) {
|
||||
foreach ($matches[1] as $name) {
|
||||
echo "- {$name}\n";
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntitySet trovato — controlla il formato XML grezzo sotto)\n";
|
||||
}
|
||||
|
||||
// ── 4. Estrai tutte le Property dell'EntityType "Cliente" (e simili) ──────
|
||||
echo "\n==================== CAMPI PER ENTITYTYPE CHE CONTENGONO 'Client' ====================\n";
|
||||
if (preg_match_all('/<EntityType Name="([^"]*[Cc]lient[^"]*)"[^>]*>(.*?)<\/EntityType>/s', $xml, $entityMatches, PREG_SET_ORDER)) {
|
||||
foreach ($entityMatches as $entity) {
|
||||
$entityName = $entity[1];
|
||||
$entityBody = $entity[2];
|
||||
echo "\n--- EntityType: {$entityName} ---\n";
|
||||
if (preg_match_all('/<Property Name="([^"]+)" Type="([^"]+)"/', $entityBody, $propMatches, PREG_SET_ORDER)) {
|
||||
foreach ($propMatches as $prop) {
|
||||
echo " {$prop[1]} ({$prop[2]})\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntityType con 'Client' nel nome trovato)\n";
|
||||
}
|
||||
|
||||
// ── 5. Salva l'XML grezzo completo per ispezione manuale, se serve ────────
|
||||
file_put_contents(__DIR__ . '/metadata_debug.xml', $xml);
|
||||
echo "\n\nXML completo salvato in: metadata_debug.xml (rimuovilo dopo il test)\n";
|
||||
@@ -1,200 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$sourceIddatadb = isset($data['source_iddatadb']) ? (int)$data['source_iddatadb'] : 0;
|
||||
$targetList = $data['target_iddatadb_list'] ?? [];
|
||||
// Default true: se il client non manda il parametro, cloniamo comunque le note
|
||||
// (comportamento di default richiesto, coerente con la checkbox pre-spuntata)
|
||||
$cloneNotes = array_key_exists('clone_notes', $data) ? !empty($data['clone_notes']) : true;
|
||||
$cloneAnalyses = !empty($data['clone_analyses']);
|
||||
$overwrite = !empty($data['overwrite']);
|
||||
|
||||
$targetIds = array_values(array_unique(array_filter(array_map('intval', (array)$targetList), function ($v) use ($sourceIddatadb) {
|
||||
return $v > 0 && $v !== $sourceIddatadb;
|
||||
})));
|
||||
|
||||
if ($sourceIddatadb <= 0 || empty($targetIds)) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Missing source or target records'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Load source parts
|
||||
$stmtParts = $pdo->prepare("
|
||||
SELECT id, part_number, part_description, mix, idmatrice, note, dateexpiry
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = ?
|
||||
AND part_description IS NOT NULL
|
||||
AND TRIM(part_description) <> ''
|
||||
ORDER BY part_number ASC, id ASC
|
||||
");
|
||||
$stmtParts->execute([$sourceIddatadb]);
|
||||
$sourceParts = $stmtParts->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if (empty($sourceParts)) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'No parts found for source record'
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 2. Prepare statements
|
||||
$stmtInsertPart = $pdo->prepare("
|
||||
INSERT INTO identification_parts
|
||||
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
|
||||
VALUES
|
||||
(:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())
|
||||
");
|
||||
|
||||
$stmtLoadCF = $pdo->prepare("
|
||||
SELECT field_id, value_id, value_text
|
||||
FROM identification_parts_customfields
|
||||
WHERE part_id = ?
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmtLoadAnalyses = $pdo->prepare("
|
||||
SELECT idmatrice, analysis_recordkey, analysis_name, analysis_method,
|
||||
analysis_level, is_web_selectable, is_accredited
|
||||
FROM identification_parts_analyses
|
||||
WHERE part_id = ?
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmtInsertAnalysis = $pdo->prepare("
|
||||
INSERT INTO identification_parts_analyses
|
||||
(part_id, iddatadb, idmatrice, analysis_recordkey, analysis_name, analysis_method,
|
||||
analysis_level, is_web_selectable, is_accredited, created_at, updated_at)
|
||||
VALUES
|
||||
(:part_id, :iddatadb, :idmatrice, :analysis_recordkey, :analysis_name, :analysis_method,
|
||||
:analysis_level, :is_web_selectable, :is_accredited, NOW(), NOW())
|
||||
");
|
||||
|
||||
$stmtInsertCF = $pdo->prepare("
|
||||
INSERT INTO identification_parts_customfields
|
||||
(part_id, field_id, value_id, value_text, created_at, updated_at)
|
||||
VALUES
|
||||
(:part_id, :field_id, :value_id, :value_text, NOW(), NOW())
|
||||
");
|
||||
|
||||
$details = [];
|
||||
$totalClonedParts = 0;
|
||||
$totalClonedAnalyses = 0;
|
||||
$totalDeletedParts = 0;
|
||||
|
||||
// 2b. Se overwrite: elimina le parti esistenti dei target (e i loro figli)
|
||||
if ($overwrite) {
|
||||
$stmtSelectExisting = $pdo->prepare("SELECT id FROM identification_parts WHERE iddatadb = ?");
|
||||
$stmtDeleteCFByPart = $pdo->prepare("DELETE FROM identification_parts_customfields WHERE part_id = ?");
|
||||
$stmtDeleteAnByPart = $pdo->prepare("DELETE FROM identification_parts_analyses WHERE part_id = ?");
|
||||
$stmtDeletePart = $pdo->prepare("DELETE FROM identification_parts WHERE id = ?");
|
||||
|
||||
foreach ($targetIds as $targetIddatadb) {
|
||||
$stmtSelectExisting->execute([$targetIddatadb]);
|
||||
$existingIds = $stmtSelectExisting->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
foreach ($existingIds as $existingPartId) {
|
||||
$existingPartId = (int)$existingPartId;
|
||||
$stmtDeleteCFByPart->execute([$existingPartId]);
|
||||
$stmtDeleteAnByPart->execute([$existingPartId]);
|
||||
$stmtDeletePart->execute([$existingPartId]);
|
||||
$totalDeletedParts++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Clone source parts to each target record
|
||||
foreach ($targetIds as $targetIddatadb) {
|
||||
$clonedCountForTarget = 0;
|
||||
|
||||
foreach ($sourceParts as $part) {
|
||||
$stmtInsertPart->execute([
|
||||
':iddatadb' => $targetIddatadb,
|
||||
':part_number' => $part['part_number'],
|
||||
':part_description' => $part['part_description'],
|
||||
':mix' => $part['mix'] ?? 'N',
|
||||
':idmatrice' => $part['idmatrice'] !== '' ? $part['idmatrice'] : null,
|
||||
':note' => $cloneNotes ? ($part['note'] ?? null) : null,
|
||||
':dateexpiry' => $part['dateexpiry'] ?? null,
|
||||
]);
|
||||
|
||||
$newPartId = (int)$pdo->lastInsertId();
|
||||
|
||||
// Load source custom fields for this part
|
||||
$stmtLoadCF->execute([(int)$part['id']]);
|
||||
$customFields = $stmtLoadCF->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($customFields as $cf) {
|
||||
$stmtInsertCF->execute([
|
||||
':part_id' => $newPartId,
|
||||
':field_id' => (int)$cf['field_id'],
|
||||
':value_id' => ($cf['value_id'] !== null && $cf['value_id'] !== '') ? (int)$cf['value_id'] : null,
|
||||
':value_text' => $cf['value_text'] !== '' ? $cf['value_text'] : null,
|
||||
]);
|
||||
}
|
||||
|
||||
// Clone linked analyses, if requested
|
||||
if ($cloneAnalyses) {
|
||||
$stmtLoadAnalyses->execute([(int)$part['id']]);
|
||||
$analyses = $stmtLoadAnalyses->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($analyses as $an) {
|
||||
$stmtInsertAnalysis->execute([
|
||||
':part_id' => $newPartId,
|
||||
':iddatadb' => $targetIddatadb,
|
||||
':idmatrice' => ($an['idmatrice'] !== null && $an['idmatrice'] !== '') ? (int)$an['idmatrice'] : null,
|
||||
':analysis_recordkey' => $an['analysis_recordkey'],
|
||||
':analysis_name' => $an['analysis_name'] ?? null,
|
||||
':analysis_method' => $an['analysis_method'] ?? null,
|
||||
':analysis_level' => ($an['analysis_level'] !== null && $an['analysis_level'] !== '') ? (int)$an['analysis_level'] : null,
|
||||
':is_web_selectable' => (int)($an['is_web_selectable'] ?? 0),
|
||||
':is_accredited' => (int)($an['is_accredited'] ?? 0),
|
||||
]);
|
||||
$totalClonedAnalyses++;
|
||||
}
|
||||
}
|
||||
|
||||
$clonedCountForTarget++;
|
||||
$totalClonedParts++;
|
||||
}
|
||||
|
||||
$details[] = [
|
||||
'target_iddatadb' => $targetIddatadb,
|
||||
'cloned_parts' => $clonedCountForTarget
|
||||
];
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'source_iddatadb' => $sourceIddatadb,
|
||||
'cloned_targets' => count($targetIds),
|
||||
'total_cloned_parts' => $totalClonedParts,
|
||||
'total_cloned_analyses' => $totalClonedAnalyses,
|
||||
'details' => $details
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Clone failed: ' . $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -1,228 +0,0 @@
|
||||
<?php
|
||||
require_once 'class/db-functions.php';
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
die("Invalid template ID");
|
||||
}
|
||||
|
||||
$sourceTemplateId = (int)$_GET['id'];
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
// 1. Get source template
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
name,
|
||||
source_type,
|
||||
header_row,
|
||||
start_column,
|
||||
description,
|
||||
target_table,
|
||||
sample_xlsx,
|
||||
button_size,
|
||||
button_bg_color,
|
||||
button_text_color,
|
||||
button_label,
|
||||
status,
|
||||
client_specific_fields,
|
||||
idclient,
|
||||
clientname,
|
||||
schemaname,
|
||||
idschema,
|
||||
schemajson,
|
||||
xls_headers,
|
||||
api_sample_json,
|
||||
json_nodes,
|
||||
idroutine
|
||||
FROM excel_templates
|
||||
WHERE id = ?
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([$sourceTemplateId]);
|
||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$template) {
|
||||
throw new Exception("Template not found.");
|
||||
}
|
||||
|
||||
// 2. Generate unique clone name
|
||||
$baseName = 'Copia di ' . $template['name'];
|
||||
$newName = $baseName;
|
||||
|
||||
$checkStmt = $pdo->prepare("SELECT COUNT(*) FROM excel_templates WHERE name = ?");
|
||||
$checkStmt->execute([$newName]);
|
||||
|
||||
if ((int)$checkStmt->fetchColumn() > 0) {
|
||||
$counter = 2;
|
||||
|
||||
do {
|
||||
$newName = $baseName . ' (' . $counter . ')';
|
||||
$checkStmt->execute([$newName]);
|
||||
$exists = (int)$checkStmt->fetchColumn() > 0;
|
||||
$counter++;
|
||||
} while ($exists);
|
||||
}
|
||||
|
||||
// 3. Insert cloned template
|
||||
$insertTemplate = $pdo->prepare("
|
||||
INSERT INTO excel_templates (
|
||||
name,
|
||||
source_type,
|
||||
created_at,
|
||||
updated_at,
|
||||
header_row,
|
||||
start_column,
|
||||
description,
|
||||
target_table,
|
||||
sample_xlsx,
|
||||
button_size,
|
||||
button_bg_color,
|
||||
button_text_color,
|
||||
button_label,
|
||||
status,
|
||||
client_specific_fields,
|
||||
idclient,
|
||||
clientname,
|
||||
schemaname,
|
||||
idschema,
|
||||
schemajson,
|
||||
xls_headers,
|
||||
api_sample_json,
|
||||
json_nodes,
|
||||
idroutine
|
||||
) VALUES (
|
||||
:name,
|
||||
:source_type,
|
||||
NOW(),
|
||||
NOW(),
|
||||
:header_row,
|
||||
:start_column,
|
||||
:description,
|
||||
:target_table,
|
||||
:sample_xlsx,
|
||||
:button_size,
|
||||
:button_bg_color,
|
||||
:button_text_color,
|
||||
:button_label,
|
||||
:status,
|
||||
:client_specific_fields,
|
||||
:idclient,
|
||||
:clientname,
|
||||
:schemaname,
|
||||
:idschema,
|
||||
:schemajson,
|
||||
:xls_headers,
|
||||
:api_sample_json,
|
||||
:json_nodes,
|
||||
:idroutine
|
||||
)
|
||||
");
|
||||
|
||||
$insertTemplate->execute([
|
||||
':name' => $newName,
|
||||
':source_type' => $template['source_type'],
|
||||
':header_row' => $template['header_row'],
|
||||
':start_column' => $template['start_column'],
|
||||
':description' => $template['description'],
|
||||
':target_table' => $template['target_table'],
|
||||
':sample_xlsx' => $template['sample_xlsx'],
|
||||
':button_size' => $template['button_size'],
|
||||
':button_bg_color' => $template['button_bg_color'],
|
||||
':button_text_color' => $template['button_text_color'],
|
||||
':button_label' => $template['button_label'],
|
||||
':status' => $template['status'],
|
||||
':client_specific_fields' => $template['client_specific_fields'],
|
||||
':idclient' => $template['idclient'],
|
||||
':clientname' => $template['clientname'],
|
||||
':schemaname' => $template['schemaname'],
|
||||
':idschema' => $template['idschema'],
|
||||
':schemajson' => $template['schemajson'],
|
||||
':xls_headers' => $template['xls_headers'],
|
||||
':api_sample_json' => $template['api_sample_json'],
|
||||
':json_nodes' => $template['json_nodes'],
|
||||
':idroutine' => $template['idroutine']
|
||||
]);
|
||||
|
||||
$newTemplateId = (int)$pdo->lastInsertId();
|
||||
|
||||
if ($newTemplateId <= 0) {
|
||||
throw new Exception("Unable to create cloned template.");
|
||||
}
|
||||
|
||||
// 4. Clone template_mapping rows
|
||||
$cloneMappings = $pdo->prepare("
|
||||
INSERT INTO template_mapping (
|
||||
template_id,
|
||||
schema_id,
|
||||
field_id,
|
||||
excel_column,
|
||||
json_node,
|
||||
is_manual,
|
||||
manual_default,
|
||||
auto_value,
|
||||
data_type,
|
||||
is_required,
|
||||
default_value,
|
||||
has_list,
|
||||
length,
|
||||
decimals,
|
||||
min_value,
|
||||
max_value,
|
||||
default_curr_date,
|
||||
tablename,
|
||||
field_label,
|
||||
main_field,
|
||||
is_visible_import,
|
||||
is_visible_parts
|
||||
)
|
||||
SELECT
|
||||
:new_template_id,
|
||||
schema_id,
|
||||
field_id,
|
||||
excel_column,
|
||||
json_node,
|
||||
is_manual,
|
||||
manual_default,
|
||||
auto_value,
|
||||
data_type,
|
||||
is_required,
|
||||
default_value,
|
||||
has_list,
|
||||
length,
|
||||
decimals,
|
||||
min_value,
|
||||
max_value,
|
||||
default_curr_date,
|
||||
tablename,
|
||||
field_label,
|
||||
main_field,
|
||||
is_visible_import,
|
||||
is_visible_parts
|
||||
FROM template_mapping
|
||||
WHERE template_id = :source_template_id
|
||||
");
|
||||
|
||||
$cloneMappings->execute([
|
||||
':new_template_id' => $newTemplateId,
|
||||
':source_template_id' => $sourceTemplateId
|
||||
]);
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
header("Location: templates_dashboard.php?cloned=1&new_id=" . $newTemplateId);
|
||||
exit;
|
||||
} catch (Exception $e) {
|
||||
if (isset($pdo) && $pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
|
||||
die("Clone error: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'));
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$templateId = (int)($input['template_id'] ?? 0);
|
||||
|
||||
if ($templateId <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid template_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If already exists, do nothing
|
||||
$stmt = $pdo->prepare("SELECT COUNT(*) FROM template_fixed_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$templateId]);
|
||||
if ((int)$stmt->fetchColumn() > 0) {
|
||||
echo json_encode(['success' => true, 'created' => 0, 'message' => 'Fixed fields already exist']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXED FIELDS STANDARD
|
||||
*/
|
||||
$fixedFields = [
|
||||
['ClienteResponsabile', 'INT'],
|
||||
['ClienteFornitore', 'INT'],
|
||||
['ClienteAnalisi', 'INT'],
|
||||
['ClienteFatturazione', 'INT'], // ← NUOVO
|
||||
['MoltiplicatorePrezzo', 'INT'],
|
||||
['AnagraficaCertestObject', 'INT'],
|
||||
['AnagraficaCertestService', 'INT'],
|
||||
['ConsegnaRichiesta', 'DATE'],
|
||||
];
|
||||
|
||||
|
||||
try {
|
||||
$pdo->beginTransaction();
|
||||
|
||||
$ins = $pdo->prepare("
|
||||
INSERT INTO template_fixed_mapping
|
||||
(template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import)
|
||||
VALUES
|
||||
(:template_id, :fixed_field_key, 1, :data_type, 1, NULL, 1)
|
||||
");
|
||||
|
||||
foreach ($fixedFields as $f) {
|
||||
$ins->execute([
|
||||
':template_id' => $templateId,
|
||||
':fixed_field_key' => $f[0],
|
||||
':data_type' => $f[1],
|
||||
]);
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
// Return rows (for client render)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmt->execute([$templateId]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'created' => count($rows), 'rows' => $rows]);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,105 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||
require_once dirname(__FILE__) . '/../class/VisualLimsApiClient.class.php'; // In root/public/userarea/class/
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
// Debug: Log the path where we expect the .env file
|
||||
$envPath = dirname(__DIR__, 3);
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Carica il file .env dalla root del progetto
|
||||
try {
|
||||
$dotenv = Dotenv::createImmutable($envPath);
|
||||
$dotenv->load();
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||
|
||||
// Recupera le variabili d'ambiente
|
||||
$dbHost = $_ENV['DB_HOST'];
|
||||
$dbName = $_ENV['DB_DATABASE'];
|
||||
$dbUser = $_ENV['DB_USERNAME'];
|
||||
$dbPass = $_ENV['DB_PASSWORD'];
|
||||
$dbPrefix = $_ENV['DB_PREFIX'];
|
||||
|
||||
// Debug: Log database connection details (excluding password)
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Connessione al database MySQL
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch (PDOException $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Endpoint per recuperare le Matrici
|
||||
$endpoint = 'Matrice';
|
||||
|
||||
// (Opzionale) aggiungi parametri
|
||||
$options = []; // es. ['$top' => 100]
|
||||
|
||||
// Debug: salva URL usato
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$query = http_build_query($options);
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Debug: Log the API response size
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - API response received, value count: ' . (isset($data['value']) ? count($data['value']) : 0) . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Salva il JSON in locale (opzionale, per debug)
|
||||
file_put_contents(__DIR__ . '/matrici_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
// Svuota la tabella (dump rapido)
|
||||
$pdo->exec("TRUNCATE TABLE {$dbPrefix}matrici");
|
||||
|
||||
// Debug: Log after truncate
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Table truncated: ' . $dbPrefix . 'matrici' . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Prepara l'insert
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO {$dbPrefix}matrici (
|
||||
IdMatrice, NomeMatriceTraduzione, DescrizioneTraduzione, MacroMatrice, NomeMatrice, Descrizione
|
||||
) VALUES (
|
||||
:IdMatrice, :NomeMatriceTraduzione, :DescrizioneTraduzione, :MacroMatrice, :NomeMatrice, :Descrizione
|
||||
)
|
||||
");
|
||||
|
||||
// Inserisci i dati
|
||||
$insertedRows = 0;
|
||||
if (isset($data['value']) && is_array($data['value'])) {
|
||||
foreach ($data['value'] as $item) {
|
||||
$stmt->execute([
|
||||
':IdMatrice' => $item['IdMatrice'],
|
||||
':NomeMatriceTraduzione' => $item['NomeMatriceTraduzione'],
|
||||
':DescrizioneTraduzione' => $item['DescrizioneTraduzione'] ?? null,
|
||||
':MacroMatrice' => $item['MacroMatrice'] ?? null,
|
||||
':NomeMatrice' => $item['NomeMatrice'],
|
||||
':Descrizione' => $item['Descrizione'] ?? null,
|
||||
]);
|
||||
$insertedRows++;
|
||||
// Debug: Log each inserted row
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Inserted row with IdMatrice: ' . $item['IdMatrice'] . PHP_EOL, FILE_APPEND);
|
||||
}
|
||||
}
|
||||
|
||||
// Log successo
|
||||
file_put_contents(__DIR__ . '/success_log.txt', date('Y-m-d H:i:s') . ' - Aggiornamento completato: ' . $insertedRows . ' record inseriti.' . PHP_EOL, FILE_APPEND);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
exit(0); // Esci con successo per cron
|
||||
@@ -1 +0,0 @@
|
||||
https://93.43.5.102/limsapi/api/odata/Matrice
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
2026-04-03 10:07:01 - Aggiornamento completato: 3198 record inseriti.
|
||||
File diff suppressed because one or more lines are too long
@@ -1,290 +0,0 @@
|
||||
<?php
|
||||
require_once "class/VisualLimsApiClient.class.php";
|
||||
include('include/headscript.php');
|
||||
|
||||
// Force HTML response
|
||||
header('Content-Type: text/html; charset=UTF-8');
|
||||
|
||||
// Initialize variables
|
||||
$error = null;
|
||||
$result = null;
|
||||
$entityType = $_GET['entity_type'] ?? 'CommessaWeb';
|
||||
$entityId = isset($_GET['entity_id']) ? (int)$_GET['entity_id'] : 0;
|
||||
$customExpand = trim($_GET['expand'] ?? '');
|
||||
$rawEndpoint = null;
|
||||
|
||||
function h($value)
|
||||
{
|
||||
return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
|
||||
}
|
||||
|
||||
function renderArrayAsTable(array $items)
|
||||
{
|
||||
if (empty($items)) {
|
||||
echo '<div class="alert alert-secondary">No records found</div>';
|
||||
return;
|
||||
}
|
||||
|
||||
$firstRow = reset($items);
|
||||
if (!is_array($firstRow)) {
|
||||
echo '<pre class="bg-light p-3 border rounded">' . h(json_encode($items, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) . '</pre>';
|
||||
return;
|
||||
}
|
||||
|
||||
$headers = [];
|
||||
foreach ($items as $row) {
|
||||
if (is_array($row)) {
|
||||
$headers = array_unique(array_merge($headers, array_keys($row)));
|
||||
}
|
||||
}
|
||||
|
||||
echo '<div class="table-responsive">';
|
||||
echo '<table class="table table-striped table-bordered table-sm align-middle">';
|
||||
echo '<thead class="table-dark"><tr>';
|
||||
foreach ($headers as $header) {
|
||||
echo '<th>' . h($header) . '</th>';
|
||||
}
|
||||
echo '</tr></thead><tbody>';
|
||||
|
||||
foreach ($items as $row) {
|
||||
echo '<tr>';
|
||||
foreach ($headers as $header) {
|
||||
$value = $row[$header] ?? '';
|
||||
if (is_array($value) || is_object($value)) {
|
||||
$value = json_encode($value, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
||||
echo '<td><pre style="white-space:pre-wrap; margin:0;">' . h($value) . '</pre></td>';
|
||||
} else {
|
||||
echo '<td>' . h($value) . '</td>';
|
||||
}
|
||||
}
|
||||
echo '</tr>';
|
||||
}
|
||||
|
||||
echo '</tbody></table>';
|
||||
echo '</div>';
|
||||
}
|
||||
|
||||
try {
|
||||
if ($entityId > 0) {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Default expands for better debugging
|
||||
if ($customExpand !== '') {
|
||||
$expand = $customExpand;
|
||||
} else {
|
||||
if ($entityType === 'CommessaWeb') {
|
||||
$expand = implode(',', [
|
||||
'CommesseCustomFields($expand=CustomField)',
|
||||
'Campioni'
|
||||
]);
|
||||
} else {
|
||||
$expand = implode(',', [
|
||||
'CommesseCustomFields($expand=CustomField)',
|
||||
'Campioni'
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
$rawEndpoint = $entityType . '(' . $entityId . ')?$expand=' . $expand;
|
||||
$result = $api->get($rawEndpoint);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$error = $e->getMessage();
|
||||
}
|
||||
?>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Debug Commessa API</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
background: #f5f7fb;
|
||||
font-family: Arial, sans-serif;
|
||||
}
|
||||
|
||||
.debug-card {
|
||||
background: #fff;
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 18px rgba(0, 0, 0, 0.08);
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
pre {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
padding: 12px;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.label-box {
|
||||
display: inline-block;
|
||||
padding: 6px 10px;
|
||||
border-radius: 8px;
|
||||
background: #eef3ff;
|
||||
color: #2446a8;
|
||||
font-weight: 600;
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.mini-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.mini-box {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e4e7eb;
|
||||
border-radius: 10px;
|
||||
padding: 12px;
|
||||
}
|
||||
|
||||
.mini-box strong {
|
||||
display: block;
|
||||
margin-bottom: 6px;
|
||||
color: #1f2937;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container py-4">
|
||||
<div class="debug-card">
|
||||
<h2 class="mb-3">Commessa / CommessaWeb API Inspector</h2>
|
||||
|
||||
<form method="GET" class="row g-3">
|
||||
<div class="col-md-3">
|
||||
<label class="form-label">Entity Type</label>
|
||||
<select name="entity_type" class="form-select">
|
||||
<option value="CommessaWeb" <?= $entityType === 'CommessaWeb' ? 'selected' : '' ?>>CommessaWeb</option>
|
||||
<option value="Commessa" <?= $entityType === 'Commessa' ? 'selected' : '' ?>>Commessa</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<label class="form-label">Entity ID</label>
|
||||
<input type="number" name="entity_id" class="form-control" value="<?= h($entityId) ?>" required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-5">
|
||||
<label class="form-label">Expand</label>
|
||||
<input type="text" name="expand" class="form-control" value="<?= h($customExpand) ?>" placeholder="CommesseCustomFields($expand=CustomField),Campioni">
|
||||
</div>
|
||||
|
||||
<div class="col-md-2 d-flex align-items-end">
|
||||
<button type="submit" class="btn btn-primary w-100">Load Data</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<?php if ($rawEndpoint): ?>
|
||||
<div class="debug-card">
|
||||
<div class="section-title">Requested Endpoint</div>
|
||||
<pre><?= h($rawEndpoint) ?></pre>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($error): ?>
|
||||
<div class="debug-card">
|
||||
<div class="alert alert-danger mb-0">
|
||||
<strong>API Error:</strong><br>
|
||||
<?= h($error) ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($result && is_array($result)): ?>
|
||||
<div class="debug-card">
|
||||
<div class="section-title">Main Information</div>
|
||||
<div class="mini-grid">
|
||||
<div class="mini-box">
|
||||
<strong>ID</strong>
|
||||
<?= h($result['IdCommessa'] ?? $result['IdCommessaWeb'] ?? '') ?>
|
||||
</div>
|
||||
<div class="mini-box">
|
||||
<strong>Code</strong>
|
||||
<?= h($result['CodiceCommessa'] ?? '') ?>
|
||||
</div>
|
||||
<div class="mini-box">
|
||||
<strong>Cliente</strong>
|
||||
<?= h($result['Cliente'] ?? '') ?>
|
||||
</div>
|
||||
<div class="mini-box">
|
||||
<strong>SchemaCustomField</strong>
|
||||
<?= h($result['SchemaCustomField'] ?? '') ?>
|
||||
</div>
|
||||
<div class="mini-box">
|
||||
<strong>Richiedente</strong>
|
||||
<?= h($result['Richiedente'] ?? '') ?>
|
||||
</div>
|
||||
<div class="mini-box">
|
||||
<strong>Descrizione</strong>
|
||||
<?= h($result['Descrizione'] ?? '') ?>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="debug-card">
|
||||
<div class="section-title">Direct Properties</div>
|
||||
<pre><?= h(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($result['CommesseCustomFields']) && is_array($result['CommesseCustomFields'])): ?>
|
||||
<div class="debug-card">
|
||||
<div class="section-title">CommesseCustomFields</div>
|
||||
<?php
|
||||
$fieldsRows = [];
|
||||
foreach ($result['CommesseCustomFields'] as $field) {
|
||||
$fieldsRows[] = [
|
||||
'IdCommesseCustomFields' => $field['IdCommesseCustomFields'] ?? '',
|
||||
'Valore' => $field['Valore'] ?? '',
|
||||
'CustomFieldId' => $field['CustomField']['IdCustomField'] ?? '',
|
||||
'Label' => $field['CustomField']['Descrizione'] ?? ($field['CustomField']['Name'] ?? ''),
|
||||
'Tipo' => $field['CustomField']['Tipo'] ?? '',
|
||||
];
|
||||
}
|
||||
renderArrayAsTable($fieldsRows);
|
||||
?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if (!empty($result['Campioni']) && is_array($result['Campioni'])): ?>
|
||||
<div class="debug-card">
|
||||
<div class="section-title">Campioni</div>
|
||||
<?php renderArrayAsTable($result['Campioni']); ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="debug-card">
|
||||
<div class="section-title">Raw JSON</div>
|
||||
<pre><?= h(json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) ?></pre>
|
||||
</div>
|
||||
<?php elseif ($entityId > 0 && !$error): ?>
|
||||
<div class="debug-card">
|
||||
<div class="alert alert-warning mb-0">No data returned from API</div>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
||||
<!-- Example of use
|
||||
debug_commessa_api.php?entity_type=CommessaWeb&entity_id=564779
|
||||
debug_commessa_api.php?entity_type=Commessa&entity_id=12345
|
||||
debug_commessa_api.php?entity_type=CommessaWeb&entity_id=564779&expand=CommesseCustomFields($expand=CustomField),Campioni
|
||||
-->
|
||||
@@ -1,152 +0,0 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* debug_odata_metadata.php — TEMPORANEO, da rimuovere dopo il test.
|
||||
* Scopo: leggere $metadata (XML standard OData) per vedere:
|
||||
* - tutti gli EntitySet disponibili (non solo "Cliente")
|
||||
* - tutti i campi (Property) definiti per l'EntityType Cliente
|
||||
* Questo bypassa qualunque vista/proiezione ristretta lato server
|
||||
* applicata sull'endpoint "Cliente" normale.
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
|
||||
$dotenv->load();
|
||||
|
||||
$baseUrl = $_ENV['API_BASE_URL'];
|
||||
$username = $_ENV['API_USERNAME'];
|
||||
$password = $_ENV['API_PASSWORD'];
|
||||
|
||||
$scriptStart = microtime(true);
|
||||
|
||||
// ── 1. Autenticazione (stessa logica della classe originale) ──────────────
|
||||
$ch = curl_init("{$baseUrl}/api/authentication/authenticate");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'Username' => $username,
|
||||
'Password' => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$authResponse = curl_exec($ch);
|
||||
$authHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($authHttpCode !== 200) {
|
||||
echo "AUTH FAILED: HTTP {$authHttpCode}\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$tokenData = json_decode($authResponse, true);
|
||||
$token = null;
|
||||
if (is_array($tokenData) && isset($tokenData['token'])) {
|
||||
$token = $tokenData['token'];
|
||||
} elseif (is_string($tokenData)) {
|
||||
$token = trim($tokenData, '"');
|
||||
} else {
|
||||
$token = trim($authResponse, '"');
|
||||
}
|
||||
|
||||
if (empty($token)) {
|
||||
echo "TOKEN NOT RECEIVED\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$authElapsed = microtime(true) - $scriptStart;
|
||||
|
||||
// ── 2. Richiesta $metadata (XML, non JSON) ─────────────────────────────────
|
||||
// Cache-buster nell'URL: alcuni server/reverse-proxy potrebbero cachare per URL esatto.
|
||||
$cacheBuster = uniqid('cb_', true);
|
||||
$metadataUrl = "{$baseUrl}/api/odata/\$metadata?_cb={$cacheBuster}";
|
||||
|
||||
$metadataStart = microtime(true);
|
||||
|
||||
$ch = curl_init($metadataUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/xml"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HEADER, true); // includi header risposta per ispezione
|
||||
|
||||
$rawResponse = curl_exec($ch);
|
||||
$curlInfo = curl_getinfo($ch);
|
||||
$httpCode = $curlInfo['http_code'];
|
||||
$curlTotalTime = $curlInfo['total_time'];
|
||||
$curlError = curl_error($ch);
|
||||
$headerSize = $curlInfo['header_size'];
|
||||
curl_close($ch);
|
||||
|
||||
$metadataElapsed = microtime(true) - $metadataStart;
|
||||
|
||||
if ($rawResponse === false || $httpCode !== 200) {
|
||||
echo "METADATA REQUEST FAILED: HTTP {$httpCode}\nError: {$curlError}\n{$rawResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$responseHeaders = substr($rawResponse, 0, $headerSize);
|
||||
$xml = substr($rawResponse, $headerSize);
|
||||
|
||||
echo "==================== TIMING (prova che NON è cache) ====================\n";
|
||||
echo "Tempo autenticazione: " . round($authElapsed * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (curl total_time): " . round($curlTotalTime * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (wall clock PHP): " . round($metadataElapsed * 1000, 1) . " ms\n";
|
||||
echo "Cache-buster usato: {$cacheBuster}\n";
|
||||
echo "Dimensione XML ricevuto: " . strlen($xml) . " bytes\n\n";
|
||||
|
||||
echo "==================== HEADER RISPOSTA (controlla se c'è Cache-Control/Age/X-Cache) ====================\n";
|
||||
echo $responseHeaders . "\n";
|
||||
|
||||
// ── 3. Estrai tutti gli EntitySet disponibili ──────────────────────────────
|
||||
echo "==================== ENTITY SETS DISPONIBILI ====================\n";
|
||||
if (preg_match_all('/<EntitySet Name="([^"]+)"/', $xml, $matches)) {
|
||||
foreach ($matches[1] as $name) {
|
||||
echo "- {$name}\n";
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntitySet trovato — controlla il formato XML grezzo sotto)\n";
|
||||
}
|
||||
|
||||
// ── 4. Estrai tutte le Property dell'EntityType "Cliente" (e simili) ──────
|
||||
echo "\n==================== CAMPI PER ENTITYTYPE CHE CONTENGONO 'Client' ====================\n";
|
||||
if (preg_match_all('/<EntityType Name="([^"]*[Cc]lient[^"]*)"[^>]*>(.*?)<\/EntityType>/s', $xml, $entityMatches, PREG_SET_ORDER)) {
|
||||
foreach ($entityMatches as $entity) {
|
||||
$entityName = $entity[1];
|
||||
$entityBody = $entity[2];
|
||||
echo "\n--- EntityType: {$entityName} ---\n";
|
||||
if (preg_match_all('/<Property Name="([^"]+)" Type="([^"]+)"/', $entityBody, $propMatches, PREG_SET_ORDER)) {
|
||||
foreach ($propMatches as $prop) {
|
||||
echo " {$prop[1]} ({$prop[2]})\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntityType con 'Client' nel nome trovato)\n";
|
||||
}
|
||||
|
||||
// ── 5. Salva l'XML grezzo completo per ispezione manuale, se serve ────────
|
||||
file_put_contents(__DIR__ . '/metadata_debug.xml', $xml);
|
||||
echo "\n\nXML completo salvato in: metadata_debug.xml (rimuovilo dopo il test)\n";
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'message' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$partId = isset($_POST['part_id']) ? (int)$_POST['part_id'] : 0;
|
||||
$analysisRecordkey = trim($_POST['analysis_recordkey'] ?? '');
|
||||
$rowId = isset($_POST['row_id']) && $_POST['row_id'] !== '' ? (int)$_POST['row_id'] : 0;
|
||||
|
||||
if ($partId <= 0 || ($analysisRecordkey === '' && $rowId <= 0)) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['success' => false, 'message' => 'Missing required data']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
if ($rowId > 0) {
|
||||
$stmt = $pdo->prepare("
|
||||
DELETE FROM identification_parts_analyses
|
||||
WHERE id = :id
|
||||
AND part_id = :part_id
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([
|
||||
':id' => $rowId,
|
||||
':part_id' => $partId,
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
DELETE FROM identification_parts_analyses
|
||||
WHERE part_id = :part_id
|
||||
AND analysis_recordkey = :analysis_recordkey
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute([
|
||||
':part_id' => $partId,
|
||||
':analysis_recordkey' => $analysisRecordkey,
|
||||
]);
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'message' => 'Association deleted'
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$partId = $data['part_id'] ?? null;
|
||||
|
||||
if (!$partId) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID parte mancante']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("DELETE FROM identification_parts WHERE id = :part_id");
|
||||
$stmt->execute([':part_id' => $partId]);
|
||||
$rowCount = $stmt->rowCount();
|
||||
if ($rowCount > 0) {
|
||||
echo json_encode(['success' => true, 'message' => 'Parte eliminata con successo']);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Nessuna parte trovata con ID ' . $partId]);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nell\'eliminazione: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -1,112 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
/**
|
||||
* Uso:
|
||||
* download_rapporto_file.php?id=123
|
||||
*/
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
|
||||
|
||||
if (!$id) {
|
||||
throw new Exception("ID RapportoFile mancante");
|
||||
}
|
||||
|
||||
$debugDir = __DIR__ . '/logs/lims_get_commessa/';
|
||||
|
||||
if (!is_dir($debugDir)) {
|
||||
mkdir($debugDir, 0755, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recupero il record RapportoFile.
|
||||
*/
|
||||
$data = $api->get("RapportoFile({$id})", []);
|
||||
|
||||
file_put_contents(
|
||||
$debugDir . "rapporto_file_{$id}.json",
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
/**
|
||||
* Provo vari nomi campo possibili per il contenuto PDF.
|
||||
* Dopo il primo test potremo adattarlo al nome esatto.
|
||||
*/
|
||||
$possibleContentFields = [
|
||||
'File',
|
||||
'file',
|
||||
'Content',
|
||||
'content',
|
||||
'FileContent',
|
||||
'fileContent',
|
||||
'Contenuto',
|
||||
'contenuto',
|
||||
'Bytes',
|
||||
'bytes'
|
||||
];
|
||||
|
||||
$base64 = null;
|
||||
|
||||
foreach ($possibleContentFields as $field) {
|
||||
if (!empty($data[$field])) {
|
||||
$base64 = $data[$field];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$base64) {
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Record RapportoFile recuperato, ma non ho trovato un campo base64 del PDF.',
|
||||
'hint' => 'Apri logs/lims_get_commessa/rapporto_file_' . $id . '.json e controlla il nome reale del campo file.',
|
||||
'data' => $data
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Se il contenuto ha prefisso data URI, lo pulisco.
|
||||
*/
|
||||
if (strpos($base64, 'base64,') !== false) {
|
||||
$parts = explode('base64,', $base64);
|
||||
$base64 = end($parts);
|
||||
}
|
||||
|
||||
$pdfBinary = base64_decode($base64, true);
|
||||
|
||||
if ($pdfBinary === false) {
|
||||
throw new Exception("Il contenuto trovato non è un base64 valido");
|
||||
}
|
||||
|
||||
$filename = $data['NomeFile']
|
||||
?? $data['nomeFile']
|
||||
?? $data['FileName']
|
||||
?? $data['fileName']
|
||||
?? "rapporto_{$id}.pdf";
|
||||
|
||||
if (!str_ends_with(strtolower($filename), '.pdf')) {
|
||||
$filename .= '.pdf';
|
||||
}
|
||||
|
||||
header('Content-Type: application/pdf');
|
||||
header('Content-Disposition: inline; filename="' . basename($filename) . '"');
|
||||
header('Content-Length: ' . strlen($pdfBinary));
|
||||
|
||||
echo $pdfBinary;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,365 +12,3 @@
|
||||
2025-08-26 16:49:24 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||
2025-08-26 16:50:23 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||
2025-09-08 08:39:17 - Risposta non JSON valida: <?xml version="1.0" encoding="utf-8"?><edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx"><edmx:DataServices><Schema Namespace="DevExpress.ExpressApp.SystemModule" xmlns="http://docs.oasis-open.org/odata/ns/edm"><EntityType Name="DashboardViewItemDescriptor"><Key><PropertyRef Name="ViewId" /></Key><Property Name="ViewId" Type="Edm.String" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem" BaseType="DevExpress.ExpressApp.NonPersistentLiteObject" Abstract="true"><Property Name="Visibility" Type="DevExpress.ExpressApp.Editors.ViewItemVisibility" Nullable="false" /></EntityType><EntityType Name="DashboardOrganizationItem_1OfIModelDashboardViewItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem" Abstract="true" /><EntityType Name="ViewDashboardOrganizationItem" BaseType="DevExpress.ExpressApp.SystemModule.DashboardOrganizationItem_1OfIModelDashboardViewItem"><Property Name="ObjectType" Type="System.Type" /><Proper
|
||||
2026-01-27 15:33:53 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-27 15:34:06 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-27 15:34:10 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-27 15:35:13 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:33:38 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:33:39 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-01-29 14:34:04 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:37:29 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:41:55 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:42:03 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:42:52 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-29 14:43:00 [ClienteResponsabile] Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-01-30 10:50:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-01-30 10:50:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-01-30 11:09:22 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-19 10:00:13 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-19 10:00:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-19 10:00:44 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-23 10:44:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:42 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:43 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:43 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:43 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:52 [AnagraficaCertestObject] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:44:53 [AnagraficaCertestService] Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:26 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:26 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:48:27 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:04 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:05 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 10:49:05 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 14:26:52 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 14:26:59 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 15:54:16 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 16:22:45 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-23 16:32:17 - Autenticazione fallita: HTTP 404, Errore cURL: , Risposta: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN""http://www.w3.org/TR/html4/strict.dtd">
|
||||
<HTML><HEAD><TITLE>Not Found</TITLE>
|
||||
<META HTTP-EQUIV="Content-Type" Content="text/html; charset=us-ascii"></HEAD>
|
||||
<BODY><h2>Not Found</h2>
|
||||
<hr><p>HTTP Error 404. The requested resource is not found.</p>
|
||||
</BODY></HTML>
|
||||
|
||||
2026-02-25 15:23:12 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-26 15:01:32 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-02-28 21:01:27 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-01 19:11:58 [AnagraficaCertestObject] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-18 15:51:45 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-19 09:50:34 [AnagraficaCertestService] Autenticazione fallita: HTTP 400, Errore cURL: , Risposta: {"title":"Bad Request","status":400,"detail":"Cannot persist the object. It was modified or deleted (purged) by another application.","instance":"POST /api/authentication/authenticate","errorCode":"96bfc1252b"}
|
||||
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||
2026-03-25 14:13:34 - Autenticazione fallita: HTTP 503, Errore cURL: , Risposta: The service is unavailable.
|
||||
2026-03-26 10:57:18 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:21 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:21 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:21 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:36 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:36 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:36 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:41 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:57:56 [AnagraficaCertestObject] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-03-26 10:58:11 [AnagraficaCertestService] Autenticazione fallita: HTTP 500, Errore cURL: , Risposta: {"title":"Internal Server Error","status":500,"detail":"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.","instance":"POST /api/authentication/authenticate","errorCode":"63ab532c6"}
|
||||
2026-04-30 15:01:25 - Errore nel recupero dati: HTTP 404, Risposta: {"title":"Not Found","status":404,"detail":"Not Found","instance":"GET /api/odata/Rapporto(2621521)","errorCode":"d25cbd678"}
|
||||
2026-04-30 15:02:04 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-04-30 15:03:19 - Errore nella richiesta: URL rejected: Malformed input to a URL function
|
||||
2026-06-16 14:29:15 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21037 ms: Couldn't connect to server, Risposta:
|
||||
|
||||
|
||||
2026-06-16 14:29:36 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21099 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:03 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21076 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:03 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21083 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:03 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21083 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:34 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21043 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:55 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21053 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:55 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21050 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:31:46 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21048 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:31:46 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21043 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:31:46 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21042 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:32:07 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21047 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:09 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21058 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:09 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21057 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:09 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21057 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:30 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21047 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:11 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21062 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:11 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21064 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:11 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21065 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:33 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21049 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:33 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21057 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:33 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21053 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:13 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21032 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:14 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21031 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:14 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21027 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:35 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21054 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:13:55 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21067 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:14:25 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21065 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:14:46 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21071 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:14:46 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21078 ms: Couldn't connect to server, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-10 08:12:24 - Errore nella richiesta: SSL connection timeout
|
||||
|
||||
@@ -1,740 +0,0 @@
|
||||
/**
|
||||
* exportLims_gridData.js — Export to LIMS using gridData (for imported.php)
|
||||
*
|
||||
* Replaces export_to_lims.js for pages that use gridData instead of DOM-rendered rows.
|
||||
* Single export + batch export (Export All) with validation.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
let pendingConfirmHandler = null;
|
||||
let batchRunning = false;
|
||||
Object.defineProperty(window, "batchRunning", { get: () => batchRunning });
|
||||
|
||||
let batchCancelled = false;
|
||||
let pendingBatchConfirmHandler = null;
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────
|
||||
|
||||
function cleanupBackdrop() {
|
||||
document.querySelectorAll(".modal-backdrop").forEach((b) => b.remove());
|
||||
document.body.classList.remove("modal-open");
|
||||
document.body.style.paddingRight = "";
|
||||
const overlay = document.querySelector(".overlay.toggle-icon");
|
||||
if (overlay) overlay.style.display = "none";
|
||||
}
|
||||
|
||||
function getGridRow(iddatadb) {
|
||||
return document.querySelector(`.grid-row[data-id="${iddatadb}"]`);
|
||||
}
|
||||
|
||||
function getRowIndexByIddatadb(iddatadb) {
|
||||
return (window.gridData || []).findIndex(
|
||||
(r) => String(r.iddatadb) === String(iddatadb),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Validation ──────────────────────────────────────────────────────
|
||||
|
||||
async function validateRows(rowsToValidate) {
|
||||
const response = await fetch("validate_export.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ rows: rowsToValidate }),
|
||||
});
|
||||
if (!response.ok)
|
||||
throw new Error(`Validation HTTP error: ${response.status}`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
function clearValidationErrors() {
|
||||
// Clear from gridData
|
||||
(window.gridData || []).forEach((row) => {
|
||||
delete row._validationErrors;
|
||||
delete row._exportError;
|
||||
});
|
||||
|
||||
document
|
||||
.querySelectorAll(".grid-cell.validation-error")
|
||||
.forEach((cell) => {
|
||||
cell.classList.remove("validation-error");
|
||||
cell.querySelectorAll(".input-validation-error").forEach((el) =>
|
||||
el.classList.remove("input-validation-error"),
|
||||
);
|
||||
const tooltip = cell.querySelector(".validation-tooltip");
|
||||
if (tooltip) tooltip.remove();
|
||||
});
|
||||
document
|
||||
.querySelectorAll(".grid-row.validation-row-error")
|
||||
.forEach((row) => row.classList.remove("validation-row-error"));
|
||||
clearAllRowErrors();
|
||||
}
|
||||
|
||||
function showValidationErrors(gridRow, iddatadb, errors) {
|
||||
// Store in gridData for re-render persistence
|
||||
const idx = getRowIndexByIddatadb(iddatadb);
|
||||
if (idx >= 0) window.gridData[idx]._validationErrors = errors;
|
||||
|
||||
if (!gridRow) return;
|
||||
gridRow.classList.add("validation-row-error");
|
||||
const messages = [];
|
||||
|
||||
errors.forEach((err) => {
|
||||
messages.push(err.message);
|
||||
if (!err.field) return;
|
||||
|
||||
let cell = null;
|
||||
if (err.field.startsWith("field_label:")) {
|
||||
const label = err.field.substring("field_label:".length);
|
||||
const headers = document.querySelectorAll(".grid-header");
|
||||
let targetIndex = null;
|
||||
headers.forEach((h) => {
|
||||
if (h.textContent.trim() === label)
|
||||
targetIndex = h.getAttribute("data-index");
|
||||
});
|
||||
if (targetIndex) {
|
||||
cell = gridRow.querySelector(
|
||||
`.grid-cell[data-index="${targetIndex}"]`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
cell = gridRow.querySelector(
|
||||
`.grid-cell[data-col="${err.field}"]`,
|
||||
);
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
cell.classList.add("validation-error");
|
||||
cell.querySelectorAll("input, select").forEach((el) =>
|
||||
el.classList.add("input-validation-error"),
|
||||
);
|
||||
let tooltip = cell.querySelector(".validation-tooltip");
|
||||
if (!tooltip) {
|
||||
tooltip = document.createElement("div");
|
||||
tooltip.className = "validation-tooltip";
|
||||
cell.appendChild(tooltip);
|
||||
}
|
||||
tooltip.textContent = err.message;
|
||||
}
|
||||
});
|
||||
|
||||
showRowError(gridRow, iddatadb, messages.join("\n"));
|
||||
}
|
||||
|
||||
// ── Send export ─────────────────────────────────────────────────────
|
||||
|
||||
async function sendExport(iddatadb, batchUuid) {
|
||||
const formData = new FormData();
|
||||
formData.append("iddatadb", iddatadb);
|
||||
if (batchUuid) formData.append("batch_uuid", batchUuid);
|
||||
|
||||
const response = await fetch("export_to_lims.php", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success) {
|
||||
// Update gridData
|
||||
const idx = getRowIndexByIddatadb(iddatadb);
|
||||
if (idx >= 0) {
|
||||
window.gridData[idx].status = "l";
|
||||
window.gridData[idx].commessaweb = data.commessaweb;
|
||||
}
|
||||
|
||||
// Update visible DOM row
|
||||
const gridRow = getGridRow(iddatadb);
|
||||
if (gridRow) {
|
||||
const statusBadge = gridRow.querySelector(
|
||||
'.grid-cell[data-col="status"] .status-badge',
|
||||
);
|
||||
if (statusBadge) {
|
||||
statusBadge.classList.remove("status-i", "status-P");
|
||||
statusBadge.classList.add("status-l");
|
||||
statusBadge.textContent = "To LIMS";
|
||||
}
|
||||
const statusCell = gridRow.querySelector(
|
||||
'.grid-cell[data-col="status"]',
|
||||
);
|
||||
if (statusCell && data.commessaweb) {
|
||||
let cwSpan = statusCell.querySelector(".commessaweb-code");
|
||||
if (!cwSpan) {
|
||||
cwSpan = document.createElement("span");
|
||||
cwSpan.className = "commessaweb-code";
|
||||
cwSpan.style.cssText =
|
||||
"display:block; font-size:0.75em; color:#555; margin-top:2px;";
|
||||
statusCell.appendChild(cwSpan);
|
||||
}
|
||||
cwSpan.textContent = data.commessaweb;
|
||||
}
|
||||
const exportBtn = gridRow.querySelector(".export-lims-btn");
|
||||
if (exportBtn) {
|
||||
exportBtn.disabled = true;
|
||||
exportBtn.style.background = "#ccc";
|
||||
exportBtn.style.cursor = "not-allowed";
|
||||
exportBtn.style.opacity = "0.5";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// ── Show result modal ───────────────────────────────────────────────
|
||||
|
||||
function showExportResult(data) {
|
||||
const el = document.getElementById("exportResponseModal");
|
||||
if (!el) return;
|
||||
const modal = new bootstrap.Modal(el, { keyboard: false });
|
||||
const msg = document.getElementById("exportResponseMessage");
|
||||
const label = document.getElementById("exportResponseModalLabel");
|
||||
|
||||
if (data.success) {
|
||||
msg.innerHTML =
|
||||
`${data.message.replace(/\n/g, "<br>")}` +
|
||||
`<br>ID CommessaWeb: ${data.idcommessaweb}` +
|
||||
`<br>Codice CommessaWeb: ${data.commessaweb}` +
|
||||
(data.totalPhotos > 0 ? `<br>Foto: ${data.totalPhotos}` : "");
|
||||
label.textContent = "Esportazione Completata";
|
||||
} else {
|
||||
msg.textContent = `Errore: ${data.message}`;
|
||||
label.textContent = "Errore Esportazione";
|
||||
}
|
||||
modal.show();
|
||||
el.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
|
||||
}
|
||||
|
||||
// ── Row UI helpers ──────────────────────────────────────────────────
|
||||
|
||||
function setRowExporting(row, active) {
|
||||
if (!row) return;
|
||||
const btnCell = row.querySelector(".button-cell");
|
||||
if (active) {
|
||||
row.classList.remove("batch-disabled");
|
||||
row.classList.add("batch-exporting");
|
||||
if (btnCell) {
|
||||
btnCell.querySelectorAll(".action-btn").forEach((b) => {
|
||||
b.dataset.prevDisplay = b.style.display;
|
||||
b.style.display = "none";
|
||||
});
|
||||
const spinner = document.createElement("span");
|
||||
spinner.className = "batch-row-spinner";
|
||||
spinner.innerHTML =
|
||||
'<i class="fas fa-spinner fa-spin"></i> Exporting...';
|
||||
btnCell.appendChild(spinner);
|
||||
}
|
||||
} else {
|
||||
row.classList.remove("batch-exporting");
|
||||
if (btnCell) {
|
||||
const spinner = btnCell.querySelector(".batch-row-spinner");
|
||||
if (spinner) spinner.remove();
|
||||
btnCell.querySelectorAll(".action-btn").forEach((b) => {
|
||||
b.style.display = b.dataset.prevDisplay || "";
|
||||
delete b.dataset.prevDisplay;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showRowError(row, iddatadb, message) {
|
||||
if (!row) return;
|
||||
row.classList.add("batch-row-error");
|
||||
const btnCell = row.querySelector(".button-cell");
|
||||
if (btnCell) {
|
||||
const old = btnCell.querySelector(".batch-error-msg");
|
||||
if (old) old.remove();
|
||||
const errorEl = document.createElement("div");
|
||||
errorEl.className = "batch-error-msg";
|
||||
errorEl.textContent = "Warning — click for details";
|
||||
errorEl.addEventListener("click", () => {
|
||||
document.getElementById("exportResponseMessage").innerHTML =
|
||||
message.replace(/\n/g, "<br>");
|
||||
document.getElementById(
|
||||
"exportResponseModalLabel",
|
||||
).textContent = "Error (id: " + iddatadb + ")";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
});
|
||||
btnCell.appendChild(errorEl);
|
||||
}
|
||||
}
|
||||
|
||||
function clearAllRowErrors() {
|
||||
document
|
||||
.querySelectorAll(".grid-row.batch-row-error")
|
||||
.forEach((row) => {
|
||||
row.classList.remove("batch-row-error");
|
||||
const msg = row.querySelector(".batch-error-msg");
|
||||
if (msg) msg.remove();
|
||||
});
|
||||
}
|
||||
|
||||
function disableAllRowButtons() {
|
||||
document
|
||||
.querySelectorAll(".grid-row[data-id]")
|
||||
.forEach((row) => row.classList.add("batch-disabled"));
|
||||
const toggle = document.querySelector(
|
||||
".actions-dropdown .dropdown-toggle",
|
||||
);
|
||||
if (toggle) {
|
||||
toggle.disabled = true;
|
||||
toggle.style.opacity = "0.5";
|
||||
toggle.style.pointerEvents = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function enableAllRowButtons() {
|
||||
document
|
||||
.querySelectorAll(".grid-row[data-id]")
|
||||
.forEach((row) => row.classList.remove("batch-disabled"));
|
||||
const toggle = document.querySelector(
|
||||
".actions-dropdown .dropdown-toggle",
|
||||
);
|
||||
if (toggle) {
|
||||
toggle.disabled = false;
|
||||
toggle.style.opacity = "";
|
||||
toggle.style.pointerEvents = "";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Single row export: validate → confirm → send ────────────────────
|
||||
|
||||
function startExportConfirmFlow(iddatadb, rowIndex) {
|
||||
const gridRow = getGridRow(iddatadb);
|
||||
clearValidationErrors();
|
||||
|
||||
if (gridRow) {
|
||||
setRowExporting(gridRow, true);
|
||||
const spinner = gridRow.querySelector(".batch-row-spinner");
|
||||
if (spinner)
|
||||
spinner.innerHTML =
|
||||
'<i class="fas fa-spinner fa-spin"></i> Validating...';
|
||||
}
|
||||
|
||||
validateRows([{ iddatadb: parseInt(iddatadb), index: rowIndex }])
|
||||
.then((validationData) => {
|
||||
if (gridRow) {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
}
|
||||
|
||||
if (!validationData.success) {
|
||||
showExportResult({
|
||||
success: false,
|
||||
message: validationData.message || "Validation error",
|
||||
});
|
||||
return;
|
||||
}
|
||||
const result = validationData.results[rowIndex];
|
||||
if (result && !result.valid) {
|
||||
if (gridRow)
|
||||
showValidationErrors(gridRow, iddatadb, result.errors);
|
||||
return;
|
||||
}
|
||||
showConfirmAndExport(iddatadb, rowIndex);
|
||||
})
|
||||
.catch((error) => {
|
||||
if (gridRow) {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
}
|
||||
showExportResult({
|
||||
success: false,
|
||||
message: "Validation error: " + error.message,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function showConfirmAndExport(iddatadb, rowIndex) {
|
||||
const confirmModalElement =
|
||||
document.getElementById("exportConfirmModal");
|
||||
if (!confirmModalElement) return;
|
||||
|
||||
const confirmModal = new bootstrap.Modal(confirmModalElement, {
|
||||
keyboard: false,
|
||||
});
|
||||
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||
confirmModal.show();
|
||||
|
||||
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||
if (!confirmBtn) {
|
||||
confirmModal.hide();
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmHandler = async () => {
|
||||
pendingConfirmHandler = null;
|
||||
confirmModal.hide();
|
||||
|
||||
const gridRow = getGridRow(iddatadb);
|
||||
if (gridRow) setRowExporting(gridRow, true);
|
||||
|
||||
try {
|
||||
const data = await sendExport(iddatadb);
|
||||
if (gridRow) {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
}
|
||||
if (!data.success)
|
||||
showRowError(
|
||||
gridRow,
|
||||
iddatadb,
|
||||
data.message || "Unknown error",
|
||||
);
|
||||
showExportResult(data);
|
||||
} catch (error) {
|
||||
if (gridRow) {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
}
|
||||
showRowError(gridRow, iddatadb, error.message);
|
||||
showExportResult({ success: false, message: error.message });
|
||||
}
|
||||
};
|
||||
|
||||
if (pendingConfirmHandler)
|
||||
confirmBtn.removeEventListener("click", pendingConfirmHandler);
|
||||
pendingConfirmHandler = confirmHandler;
|
||||
confirmBtn.addEventListener("click", confirmHandler, { once: true });
|
||||
}
|
||||
|
||||
// ── Single row click (event delegation) ─────────────────────────────
|
||||
|
||||
$(document).on("click", ".export-lims-btn", function (e) {
|
||||
e.preventDefault();
|
||||
if (batchRunning) return;
|
||||
|
||||
const iddatadb = this.dataset.iddatadb;
|
||||
const rowIndex = parseInt(this.dataset.row);
|
||||
const gridRow = getGridRow(iddatadb);
|
||||
|
||||
// Check unsaved changes for this row
|
||||
const dataRow = window.gridData?.[rowIndex];
|
||||
if (dataRow && dataRow._dirty) {
|
||||
const unsavedModal = new bootstrap.Modal(
|
||||
document.getElementById("exportUnsavedModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
unsavedModal.show();
|
||||
|
||||
document.getElementById("saveAndExportBtn")?.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
unsavedModal.hide();
|
||||
// Save first, then export
|
||||
const formData = window.buildSavePayload(rowIndex);
|
||||
fetch("save_edited_row.php", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
})
|
||||
.then((r) => r.json())
|
||||
.then((result) => {
|
||||
if (result.success) {
|
||||
dataRow._dirty = false;
|
||||
startExportConfirmFlow(iddatadb, rowIndex);
|
||||
} else {
|
||||
alert("Save failed: " + result.message);
|
||||
}
|
||||
});
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
startExportConfirmFlow(iddatadb, rowIndex);
|
||||
});
|
||||
|
||||
// ── Batch export (Export All) ───────────────────────────────────────
|
||||
|
||||
function collectEligibleRows() {
|
||||
// Read from gridData, not DOM. Se i filtri sono attivi, limita al target set.
|
||||
const target =
|
||||
typeof window.getTargetGridIds === "function"
|
||||
? window.getTargetGridIds()
|
||||
: null;
|
||||
const eligible = [];
|
||||
(window.gridData || []).forEach((row, index) => {
|
||||
if (row.status === "l") return;
|
||||
if (target && !target.has(parseInt(row.iddatadb, 10))) return;
|
||||
eligible.push({
|
||||
iddatadb: row.iddatadb,
|
||||
index,
|
||||
row: getGridRow(row.iddatadb),
|
||||
});
|
||||
});
|
||||
return eligible;
|
||||
}
|
||||
|
||||
function hasUnsavedChanges() {
|
||||
const target =
|
||||
typeof window.getTargetGridIds === "function"
|
||||
? window.getTargetGridIds()
|
||||
: null;
|
||||
return (window.gridData || []).some(
|
||||
(r) =>
|
||||
r._dirty && (!target || target.has(parseInt(r.iddatadb, 10))),
|
||||
);
|
||||
}
|
||||
|
||||
async function validateAndFilter(eligibleRows) {
|
||||
const rowsToValidate = eligibleRows.map(({ iddatadb, index }) => ({
|
||||
iddatadb: parseInt(iddatadb),
|
||||
index,
|
||||
}));
|
||||
const validationData = await validateRows(rowsToValidate);
|
||||
if (!validationData.success)
|
||||
throw new Error(validationData.message || "Validation error");
|
||||
|
||||
const validRows = [];
|
||||
let invalidCount = 0;
|
||||
|
||||
for (const item of eligibleRows) {
|
||||
const result = validationData.results[item.index];
|
||||
if (result && !result.valid) {
|
||||
if (item.row)
|
||||
showValidationErrors(
|
||||
item.row,
|
||||
item.iddatadb,
|
||||
result.errors,
|
||||
);
|
||||
invalidCount++;
|
||||
} else {
|
||||
validRows.push(item);
|
||||
}
|
||||
}
|
||||
return { validRows, invalidCount };
|
||||
}
|
||||
|
||||
function showValidationSpinner(show) {
|
||||
const bar = document.getElementById("batchExportBar");
|
||||
const statusEl = document.getElementById("batchExportStatus");
|
||||
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||
if (show) {
|
||||
if (bar) bar.style.display = "";
|
||||
if (statusEl) statusEl.textContent = "Validating...";
|
||||
if (cancelBtn) cancelBtn.style.display = "none";
|
||||
} else {
|
||||
if (bar) bar.style.display = "none";
|
||||
if (cancelBtn) cancelBtn.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
function showBatchConfirm(eligibleRows) {
|
||||
clearValidationErrors();
|
||||
showValidationSpinner(true);
|
||||
|
||||
validateAndFilter(eligibleRows)
|
||||
.then(({ validRows, invalidCount }) => {
|
||||
showValidationSpinner(false);
|
||||
if (validRows.length === 0) {
|
||||
document.getElementById("exportResponseMessage").innerHTML =
|
||||
`No valid rows for export.<br><strong>${invalidCount}</strong> rows with validation errors.`;
|
||||
document.getElementById(
|
||||
"exportResponseModalLabel",
|
||||
).textContent = "Validation Failed";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmModal = new bootstrap.Modal(
|
||||
document.getElementById("exportBatchConfirmModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
let countText = String(validRows.length);
|
||||
if (invalidCount > 0)
|
||||
countText += ` (${invalidCount} excluded due to errors)`;
|
||||
document.getElementById("exportBatchCount").textContent =
|
||||
countText;
|
||||
confirmModal.show();
|
||||
|
||||
const confirmBtn = document.getElementById(
|
||||
"exportBatchConfirmBtn",
|
||||
);
|
||||
if (pendingBatchConfirmHandler)
|
||||
confirmBtn.removeEventListener(
|
||||
"click",
|
||||
pendingBatchConfirmHandler,
|
||||
);
|
||||
pendingBatchConfirmHandler = () => {
|
||||
pendingBatchConfirmHandler = null;
|
||||
confirmModal.hide();
|
||||
startBatchExport(validRows);
|
||||
};
|
||||
confirmBtn.addEventListener(
|
||||
"click",
|
||||
pendingBatchConfirmHandler,
|
||||
{ once: true },
|
||||
);
|
||||
})
|
||||
.catch((error) => {
|
||||
showValidationSpinner(false);
|
||||
document.getElementById("exportResponseMessage").textContent =
|
||||
"Validation error: " + error.message;
|
||||
document.getElementById(
|
||||
"exportResponseModalLabel",
|
||||
).textContent = "Validation Error";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on("click", ".export-all-lims-btn", function (e) {
|
||||
e.preventDefault();
|
||||
if (batchRunning) return;
|
||||
|
||||
if (hasUnsavedChanges()) {
|
||||
const unsavedModal = new bootstrap.Modal(
|
||||
document.getElementById("exportBatchUnsavedModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
unsavedModal.show();
|
||||
document.getElementById("batchSaveAndExportBtn")?.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
unsavedModal.hide();
|
||||
// Trigger save all first — listen for completion
|
||||
alert("Please Save All first, then Export All.");
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const eligibleRows = collectEligibleRows();
|
||||
if (eligibleRows.length === 0) {
|
||||
document.getElementById("exportResponseMessage").textContent =
|
||||
"All rows already exported to LIMS.";
|
||||
document.getElementById("exportResponseModalLabel").textContent =
|
||||
"Export All";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
return;
|
||||
}
|
||||
showBatchConfirm(eligibleRows);
|
||||
});
|
||||
|
||||
function startBatchExport(eligibleRows) {
|
||||
batchCancelled = false;
|
||||
batchRunning = true;
|
||||
const batchUuid = crypto.randomUUID();
|
||||
const total = eligibleRows.length;
|
||||
let processed = 0,
|
||||
succeeded = 0,
|
||||
failed = 0;
|
||||
|
||||
disableAllRowButtons();
|
||||
|
||||
const bar = document.getElementById("batchExportBar");
|
||||
const statusEl = document.getElementById("batchExportStatus");
|
||||
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||
if (bar) bar.style.display = "";
|
||||
if (cancelBtn) cancelBtn.disabled = false;
|
||||
if (statusEl) statusEl.textContent = `Exporting 0 / ${total}...`;
|
||||
|
||||
cancelBtn?.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
batchCancelled = true;
|
||||
if (statusEl)
|
||||
statusEl.textContent =
|
||||
"Cancelling... (waiting for current row)";
|
||||
if (cancelBtn) cancelBtn.disabled = true;
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
|
||||
(async () => {
|
||||
for (let i = 0; i < eligibleRows.length; i++) {
|
||||
if (batchCancelled) break;
|
||||
|
||||
const { iddatadb, row } = eligibleRows[i];
|
||||
if (statusEl)
|
||||
statusEl.textContent = `Exporting ${processed + 1} / ${total} (id: ${iddatadb})...`;
|
||||
|
||||
const gridRow = row || getGridRow(iddatadb);
|
||||
if (gridRow) setRowExporting(gridRow, true);
|
||||
|
||||
try {
|
||||
const data = await sendExport(iddatadb, batchUuid);
|
||||
processed++;
|
||||
if (data.success) {
|
||||
succeeded++;
|
||||
} else {
|
||||
failed++;
|
||||
const errIdx = getRowIndexByIddatadb(iddatadb);
|
||||
if (errIdx >= 0)
|
||||
window.gridData[errIdx]._exportError =
|
||||
data.message || "Unknown error";
|
||||
if (gridRow)
|
||||
showRowError(
|
||||
gridRow,
|
||||
iddatadb,
|
||||
data.message || "Unknown error",
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
processed++;
|
||||
failed++;
|
||||
const errIdx = getRowIndexByIddatadb(iddatadb);
|
||||
if (errIdx >= 0)
|
||||
window.gridData[errIdx]._exportError = error.message;
|
||||
if (gridRow) showRowError(gridRow, iddatadb, error.message);
|
||||
}
|
||||
|
||||
if (gridRow) {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
}
|
||||
}
|
||||
|
||||
batchRunning = false;
|
||||
enableAllRowButtons();
|
||||
if (bar) bar.style.display = "none";
|
||||
|
||||
// Re-render to reflect status changes, then restore error indicators
|
||||
const gr = window.gridRenderer;
|
||||
if (gr) gr.renderVisibleRows();
|
||||
|
||||
// Restore error state from gridData._exportError
|
||||
(window.gridData || []).forEach((row, idx) => {
|
||||
if (row._exportError) {
|
||||
const gridRow = getGridRow(row.iddatadb);
|
||||
if (gridRow)
|
||||
showRowError(gridRow, row.iddatadb, row._exportError);
|
||||
}
|
||||
});
|
||||
|
||||
const msgEl = document.getElementById("exportResponseMessage");
|
||||
const labelEl = document.getElementById("exportResponseModalLabel");
|
||||
|
||||
if (batchCancelled) {
|
||||
labelEl.textContent = "Export All — Cancelled";
|
||||
msgEl.innerHTML = `Exported: <strong>${succeeded}</strong><br>Errors: <strong>${failed}</strong><br>Not processed: <strong>${total - processed}</strong>`;
|
||||
} else if (failed === 0) {
|
||||
labelEl.textContent = "Export All — Complete";
|
||||
msgEl.innerHTML = `All <strong>${succeeded}</strong> rows exported successfully.`;
|
||||
} else {
|
||||
labelEl.textContent = "Export All — Completed with errors";
|
||||
msgEl.innerHTML = `Exported: <strong>${succeeded}</strong><br>Errors: <strong>${failed}</strong>`;
|
||||
}
|
||||
|
||||
const modalEl = document.getElementById("exportResponseModal");
|
||||
new bootstrap.Modal(modalEl, { keyboard: false }).show();
|
||||
modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, {
|
||||
once: true,
|
||||
});
|
||||
})();
|
||||
}
|
||||
})();
|
||||
@@ -1,800 +0,0 @@
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
console.log("export_to_lims.js loaded");
|
||||
|
||||
const exportButtons = document.querySelectorAll(".export-lims-btn");
|
||||
console.log(`Found ${exportButtons.length} export-lims-btn buttons`);
|
||||
|
||||
// Tracks the active confirm handler so it can be replaced on re-open
|
||||
let pendingConfirmHandler = null;
|
||||
let batchRunning = false;
|
||||
// Expose for Save All to check
|
||||
Object.defineProperty(window, "batchRunning", {
|
||||
get: () => batchRunning,
|
||||
});
|
||||
|
||||
// ── Helpers ──────────────────────────────────────────────────────────────
|
||||
|
||||
function cleanupBackdrop() {
|
||||
document.querySelectorAll(".modal-backdrop").forEach((b) => b.remove());
|
||||
document.body.classList.remove("modal-open");
|
||||
document.body.style.paddingRight = "";
|
||||
const overlay = document.querySelector(".overlay.toggle-icon");
|
||||
if (overlay) overlay.style.display = "none";
|
||||
}
|
||||
|
||||
// ── Validation ───────────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Call the validation endpoint for an array of { iddatadb, index } objects.
|
||||
* Returns the parsed JSON response.
|
||||
*/
|
||||
async function validateRows(rowsToValidate) {
|
||||
const response = await fetch("validate_export.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ rows: rowsToValidate }),
|
||||
});
|
||||
if (!response.ok)
|
||||
throw new Error(`Validation HTTP error: ${response.status}`);
|
||||
return response.json();
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all validation-error highlights from the grid.
|
||||
*/
|
||||
function clearValidationErrors() {
|
||||
document.querySelectorAll(".grid-cell.validation-error").forEach((cell) => {
|
||||
cell.classList.remove("validation-error");
|
||||
cell.querySelectorAll(".input-validation-error").forEach((el) => {
|
||||
el.classList.remove("input-validation-error");
|
||||
});
|
||||
const tooltip = cell.querySelector(".validation-tooltip");
|
||||
if (tooltip) tooltip.remove();
|
||||
});
|
||||
document.querySelectorAll(".grid-row.validation-row-error").forEach((row) => {
|
||||
row.classList.remove("validation-row-error");
|
||||
});
|
||||
// Also clear batch-row-error that came from validation
|
||||
clearAllRowErrors();
|
||||
}
|
||||
|
||||
/**
|
||||
* Highlight specific cells on a row based on validation errors.
|
||||
* Each error has { field, message }.
|
||||
* field can be: a data-col value, "parts", "field_label:SomeLabel", or null (row-level).
|
||||
*/
|
||||
function showValidationErrors(row, iddatadb, errors) {
|
||||
row.classList.add("validation-row-error");
|
||||
|
||||
const messages = [];
|
||||
|
||||
errors.forEach((err) => {
|
||||
messages.push(err.message);
|
||||
|
||||
if (!err.field) return; // row-level error, no specific cell
|
||||
|
||||
let cell = null;
|
||||
|
||||
if (err.field === "parts") {
|
||||
// No specific cell to highlight, but we highlight the parts button area
|
||||
// just add to messages
|
||||
return;
|
||||
} else if (err.field.startsWith("field_label:")) {
|
||||
// Match by field_label text — find the header with this label, get its index
|
||||
const label = err.field.substring("field_label:".length);
|
||||
const headers = document.querySelectorAll(".grid-header");
|
||||
let targetIndex = null;
|
||||
headers.forEach((h) => {
|
||||
if (h.textContent.trim() === label) {
|
||||
targetIndex = h.getAttribute("data-index");
|
||||
}
|
||||
});
|
||||
if (targetIndex) {
|
||||
const rowIndex = row.querySelector(".grid-cell[data-row]")?.getAttribute("data-row");
|
||||
if (rowIndex !== null) {
|
||||
cell = row.querySelector(`.grid-cell[data-row="${rowIndex}"][data-index="${targetIndex}"]`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Direct data-col match
|
||||
const rowIndex = row.querySelector(".grid-cell[data-row]")?.getAttribute("data-row");
|
||||
if (rowIndex !== null) {
|
||||
cell = row.querySelector(`.grid-cell[data-col="${err.field}"][data-row="${rowIndex}"]`);
|
||||
}
|
||||
}
|
||||
|
||||
if (cell) {
|
||||
cell.classList.add("validation-error");
|
||||
// Mark the input/select inside the cell
|
||||
cell.querySelectorAll("input, select").forEach((el) => {
|
||||
el.classList.add("input-validation-error");
|
||||
});
|
||||
// Add tooltip with error message
|
||||
let tooltip = cell.querySelector(".validation-tooltip");
|
||||
if (!tooltip) {
|
||||
tooltip = document.createElement("div");
|
||||
tooltip.className = "validation-tooltip";
|
||||
cell.appendChild(tooltip);
|
||||
}
|
||||
tooltip.textContent = err.message;
|
||||
}
|
||||
});
|
||||
|
||||
// Show aggregated error on the row using existing mechanism
|
||||
showRowError(row, iddatadb, messages.join("\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a single export request and update the row UI on success.
|
||||
* Returns the parsed JSON response.
|
||||
*/
|
||||
async function sendExport(iddatadb, gridRow, batchUuid = null) {
|
||||
const formData = new FormData();
|
||||
formData.append("iddatadb", iddatadb);
|
||||
if (batchUuid) {
|
||||
formData.append("batch_uuid", batchUuid);
|
||||
}
|
||||
|
||||
const response = await fetch("export_to_lims.php", {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
});
|
||||
if (!response.ok)
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
const data = await response.json();
|
||||
|
||||
if (data.success && gridRow) {
|
||||
// Update status badge
|
||||
const statusBadge = gridRow.querySelector(
|
||||
'.grid-cell[data-col="status"] .status-badge',
|
||||
);
|
||||
if (statusBadge) {
|
||||
statusBadge.classList.remove("status-i", "status-P");
|
||||
statusBadge.classList.add("status-l");
|
||||
statusBadge.textContent = "To LIMS";
|
||||
}
|
||||
|
||||
// Insert/update CommessaWeb code span
|
||||
const statusCell = gridRow.querySelector(
|
||||
'.grid-cell[data-col="status"]',
|
||||
);
|
||||
if (statusCell && data.commessaweb) {
|
||||
let cwSpan = statusCell.querySelector(".commessaweb-code");
|
||||
if (!cwSpan) {
|
||||
cwSpan = document.createElement("span");
|
||||
cwSpan.className = "commessaweb-code";
|
||||
cwSpan.style.cssText =
|
||||
"display:block; font-size:0.75em; color:#555; margin-top:2px;";
|
||||
cwSpan.title = "CommessaWeb";
|
||||
const hiddenInput = statusCell.querySelector(
|
||||
'input[type="hidden"]',
|
||||
);
|
||||
hiddenInput
|
||||
? statusCell.insertBefore(cwSpan, hiddenInput)
|
||||
: statusCell.appendChild(cwSpan);
|
||||
}
|
||||
cwSpan.textContent = data.commessaweb;
|
||||
}
|
||||
|
||||
// Disable export button for this row
|
||||
const exportBtn = gridRow.querySelector(".export-lims-btn");
|
||||
if (exportBtn) {
|
||||
exportBtn.disabled = true;
|
||||
exportBtn.style.background = "#ccc";
|
||||
exportBtn.style.cursor = "not-allowed";
|
||||
exportBtn.style.opacity = "0.5";
|
||||
exportBtn.title = "Già esportato";
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the result of a single export in the response modal.
|
||||
*/
|
||||
function showExportResult(data) {
|
||||
const responseModalElement =
|
||||
document.getElementById("exportResponseModal");
|
||||
if (!responseModalElement) {
|
||||
alert("Errore: Modale di risposta non trovato");
|
||||
return;
|
||||
}
|
||||
|
||||
const responseModal = new bootstrap.Modal(responseModalElement, {
|
||||
keyboard: false,
|
||||
});
|
||||
const responseMessage = document.getElementById(
|
||||
"exportResponseMessage",
|
||||
);
|
||||
|
||||
if (data.success) {
|
||||
responseMessage.innerHTML =
|
||||
`${data.message.replace(/\n/g, "<br>")}` +
|
||||
`<br>ID CommessaWeb: ${data.idcommessaweb}` +
|
||||
`<br>Codice CommessaWeb: ${data.commessaweb}` +
|
||||
(data.totalPhotos > 0
|
||||
? `<br>Foto trovate: ${data.totalPhotos}`
|
||||
: "");
|
||||
document.getElementById("exportResponseModalLabel").textContent =
|
||||
"Esportazione Completata";
|
||||
} else {
|
||||
responseMessage.textContent = `Errore durante la generazione dei payload: ${data.message}`;
|
||||
document.getElementById("exportResponseModalLabel").textContent =
|
||||
"Errore Esportazione";
|
||||
}
|
||||
|
||||
responseModal.show();
|
||||
responseModalElement.addEventListener(
|
||||
"hidden.bs.modal",
|
||||
cleanupBackdrop,
|
||||
{ once: true },
|
||||
);
|
||||
}
|
||||
|
||||
// ── Row button helpers (disable/enable during batch) ────────────────────
|
||||
|
||||
function setRowExporting(row, active) {
|
||||
const btnCell = row.querySelector(".button-cell");
|
||||
if (active) {
|
||||
row.classList.remove("batch-disabled");
|
||||
row.classList.add("batch-exporting");
|
||||
if (btnCell) {
|
||||
btnCell.querySelectorAll(".action-btn").forEach((b) => {
|
||||
b.dataset.prevDisplay = b.style.display;
|
||||
b.style.display = "none";
|
||||
});
|
||||
const spinner = document.createElement("span");
|
||||
spinner.className = "batch-row-spinner";
|
||||
spinner.innerHTML =
|
||||
'<i class="fas fa-spinner fa-spin"></i> Exporting...';
|
||||
btnCell.appendChild(spinner);
|
||||
}
|
||||
} else {
|
||||
row.classList.remove("batch-exporting");
|
||||
row.classList.add("batch-disabled");
|
||||
if (btnCell) {
|
||||
const spinner = btnCell.querySelector(".batch-row-spinner");
|
||||
if (spinner) spinner.remove();
|
||||
btnCell.querySelectorAll(".action-btn").forEach((b) => {
|
||||
b.style.display = b.dataset.prevDisplay || "";
|
||||
delete b.dataset.prevDisplay;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function showRowError(row, iddatadb, message) {
|
||||
row.classList.add("batch-row-error");
|
||||
const btnCell = row.querySelector(".button-cell");
|
||||
if (btnCell) {
|
||||
// Remove existing error msg
|
||||
const old = btnCell.querySelector(".batch-error-msg");
|
||||
if (old) old.remove();
|
||||
|
||||
const errorEl = document.createElement("div");
|
||||
errorEl.className = "batch-error-msg";
|
||||
errorEl.textContent = "⚠ Errore — clicca per dettagli";
|
||||
errorEl.addEventListener("click", () => {
|
||||
document.getElementById("exportResponseMessage").innerHTML = message.replace(/\n/g, "<br>");
|
||||
document.getElementById("exportResponseModalLabel").textContent = "Errore Validazione (id: " + iddatadb + ")";
|
||||
new bootstrap.Modal(document.getElementById("exportResponseModal"), { keyboard: false }).show();
|
||||
});
|
||||
btnCell.appendChild(errorEl);
|
||||
}
|
||||
}
|
||||
|
||||
function clearAllRowErrors() {
|
||||
document.querySelectorAll(".grid-row.batch-row-error").forEach((row) => {
|
||||
row.classList.remove("batch-row-error");
|
||||
const msg = row.querySelector(".batch-error-msg");
|
||||
if (msg) msg.remove();
|
||||
});
|
||||
}
|
||||
|
||||
function disableAllRowButtons() {
|
||||
document.querySelectorAll(".grid-row[data-id]").forEach((row) => {
|
||||
row.classList.add("batch-disabled");
|
||||
});
|
||||
// Disable Actions dropdown
|
||||
const toggle = document.querySelector(
|
||||
".actions-dropdown .dropdown-toggle",
|
||||
);
|
||||
if (toggle) {
|
||||
toggle.disabled = true;
|
||||
toggle.style.opacity = "0.5";
|
||||
toggle.style.pointerEvents = "none";
|
||||
}
|
||||
}
|
||||
|
||||
function enableAllRowButtons() {
|
||||
document.querySelectorAll(".grid-row[data-id]").forEach((row) => {
|
||||
row.classList.remove("batch-disabled");
|
||||
});
|
||||
const toggle = document.querySelector(
|
||||
".actions-dropdown .dropdown-toggle",
|
||||
);
|
||||
if (toggle) {
|
||||
toggle.disabled = false;
|
||||
toggle.style.opacity = "";
|
||||
toggle.style.pointerEvents = "";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Single row export: validate, confirm modal, then send ───────────────
|
||||
|
||||
function startExportConfirmFlow(iddatadb, btn) {
|
||||
const gridRow = btn.closest(".grid-row");
|
||||
const rowIndex = btn.dataset.row;
|
||||
|
||||
// Validate first
|
||||
clearValidationErrors();
|
||||
|
||||
// Show validating state on the row
|
||||
setRowExporting(gridRow, true);
|
||||
const spinner = gridRow.querySelector(".batch-row-spinner");
|
||||
if (spinner) spinner.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Validating...';
|
||||
|
||||
validateRows([{ iddatadb: parseInt(iddatadb), index: parseInt(rowIndex) }])
|
||||
.then((validationData) => {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
|
||||
if (!validationData.success) {
|
||||
showExportResult({ success: false, message: validationData.message || "Errore di validazione" });
|
||||
return;
|
||||
}
|
||||
|
||||
const result = validationData.results[rowIndex];
|
||||
if (result && !result.valid) {
|
||||
// Show validation errors on the row
|
||||
showValidationErrors(gridRow, iddatadb, result.errors);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validation passed — show confirm modal
|
||||
showConfirmAndExport(iddatadb, btn);
|
||||
})
|
||||
.catch((error) => {
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
|
||||
console.error("Validation error:", error);
|
||||
showExportResult({ success: false, message: "Errore di validazione: " + error.message });
|
||||
});
|
||||
}
|
||||
|
||||
function showConfirmAndExport(iddatadb, btn) {
|
||||
const confirmModalElement =
|
||||
document.getElementById("exportConfirmModal");
|
||||
if (!confirmModalElement) {
|
||||
alert("Errore: Modale di conferma non trovato");
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmModal = new bootstrap.Modal(confirmModalElement, {
|
||||
keyboard: false,
|
||||
});
|
||||
document.getElementById("exportIddatadb").textContent = iddatadb;
|
||||
confirmModal.show();
|
||||
|
||||
const confirmBtn = document.getElementById("exportConfirmBtn");
|
||||
if (!confirmBtn) {
|
||||
confirmModal.hide();
|
||||
alert("Errore: Pulsante di conferma non trovato");
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmHandler = async () => {
|
||||
pendingConfirmHandler = null;
|
||||
console.log(`Confirmed export for iddatadb: ${iddatadb}`);
|
||||
confirmModal.hide();
|
||||
|
||||
const gridRow = btn.closest(".grid-row");
|
||||
setRowExporting(gridRow, true);
|
||||
|
||||
try {
|
||||
const data = await sendExport(iddatadb, gridRow);
|
||||
console.log("Export response:", data);
|
||||
|
||||
// Stop spinner, fully restore row
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
|
||||
if (!data.success) {
|
||||
showRowError(gridRow, iddatadb, data.message || "Errore sconosciuto");
|
||||
}
|
||||
showExportResult(data);
|
||||
} catch (error) {
|
||||
console.error("Export error:", error);
|
||||
|
||||
// Stop spinner, fully restore row so user can retry
|
||||
setRowExporting(gridRow, false);
|
||||
gridRow.classList.remove("batch-disabled");
|
||||
|
||||
showRowError(gridRow, iddatadb, error.message);
|
||||
showExportResult({
|
||||
success: false,
|
||||
message: error.message,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
if (pendingConfirmHandler) {
|
||||
confirmBtn.removeEventListener("click", pendingConfirmHandler);
|
||||
}
|
||||
pendingConfirmHandler = confirmHandler;
|
||||
confirmBtn.addEventListener("click", confirmHandler, { once: true });
|
||||
}
|
||||
|
||||
// ── Single row click handler ────────────────────────────────────────────
|
||||
|
||||
exportButtons.forEach((btn) => {
|
||||
btn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (batchRunning) return;
|
||||
|
||||
const rowIndex = btn.dataset.row;
|
||||
const iddatadb = btn.dataset.iddatadb;
|
||||
console.log(
|
||||
`Export to LIMS clicked for row ${rowIndex}, iddatadb: ${iddatadb}`,
|
||||
);
|
||||
|
||||
const gridRow = btn.closest(".grid-row");
|
||||
|
||||
if (gridRow && gridRow.querySelector(".cell-changed")) {
|
||||
const unsavedModal = new bootstrap.Modal(
|
||||
document.getElementById("exportUnsavedModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
unsavedModal.show();
|
||||
|
||||
document.getElementById("saveAndExportBtn").addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
unsavedModal.hide();
|
||||
const saveBtn = gridRow.querySelector(".save-btn");
|
||||
if (!saveBtn) return;
|
||||
|
||||
const observer = new MutationObserver(() => {
|
||||
if (!gridRow.querySelector(".cell-changed")) {
|
||||
observer.disconnect();
|
||||
startExportConfirmFlow(iddatadb, btn);
|
||||
}
|
||||
});
|
||||
observer.observe(gridRow, {
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
});
|
||||
|
||||
saveBtn.click();
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// No unsaved changes — go straight to validate + export confirm
|
||||
startExportConfirmFlow(iddatadb, btn);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Batch export (Export All) ───────────────────────────────────────────
|
||||
|
||||
const exportAllBtn = document.querySelector(".export-all-lims-btn");
|
||||
if (!exportAllBtn) return;
|
||||
|
||||
let batchCancelled = false;
|
||||
let pendingBatchConfirmHandler = null;
|
||||
|
||||
function collectEligibleRows() {
|
||||
const allRows = document.querySelectorAll(".grid-row[data-id]");
|
||||
const eligible = [];
|
||||
allRows.forEach((row) => {
|
||||
const statusBadge = row.querySelector(
|
||||
'.grid-cell[data-col="status"] .status-badge',
|
||||
);
|
||||
if (statusBadge && !statusBadge.classList.contains("status-l")) {
|
||||
const iddatadb = row.dataset.id;
|
||||
if (iddatadb) {
|
||||
eligible.push({ iddatadb, row });
|
||||
}
|
||||
}
|
||||
});
|
||||
return eligible;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data-row index for a grid row element.
|
||||
*/
|
||||
function getRowIndex(row) {
|
||||
const cell = row.querySelector(".grid-cell[data-row]");
|
||||
return cell ? parseInt(cell.getAttribute("data-row")) : null;
|
||||
}
|
||||
|
||||
function hasUnsavedChanges() {
|
||||
return !!document.querySelector(".grid-row[data-id] .cell-changed");
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate all eligible rows, show errors, and return only the valid ones.
|
||||
*/
|
||||
async function validateAndFilter(eligibleRows) {
|
||||
const rowsToValidate = eligibleRows.map(({ iddatadb, row }) => ({
|
||||
iddatadb: parseInt(iddatadb),
|
||||
index: getRowIndex(row),
|
||||
}));
|
||||
|
||||
const validationData = await validateRows(rowsToValidate);
|
||||
|
||||
if (!validationData.success) {
|
||||
throw new Error(validationData.message || "Errore di validazione");
|
||||
}
|
||||
|
||||
const validRows = [];
|
||||
let invalidCount = 0;
|
||||
|
||||
for (const { iddatadb, row } of eligibleRows) {
|
||||
const rowIdx = getRowIndex(row);
|
||||
const result = validationData.results[rowIdx];
|
||||
|
||||
if (result && !result.valid) {
|
||||
showValidationErrors(row, iddatadb, result.errors);
|
||||
invalidCount++;
|
||||
} else {
|
||||
validRows.push({ iddatadb, row });
|
||||
}
|
||||
}
|
||||
|
||||
return { validRows, invalidCount };
|
||||
}
|
||||
|
||||
function showValidationSpinner(show) {
|
||||
const bar = document.getElementById("batchExportBar");
|
||||
const statusEl = document.getElementById("batchExportStatus");
|
||||
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||
if (show) {
|
||||
bar.style.display = "";
|
||||
statusEl.textContent = "Validazione in corso...";
|
||||
cancelBtn.style.display = "none";
|
||||
} else {
|
||||
bar.style.display = "none";
|
||||
cancelBtn.style.display = "";
|
||||
}
|
||||
}
|
||||
|
||||
function showBatchConfirm(eligibleRows) {
|
||||
clearValidationErrors();
|
||||
showValidationSpinner(true);
|
||||
|
||||
// Validate before showing confirm
|
||||
validateAndFilter(eligibleRows)
|
||||
.then(({ validRows, invalidCount }) => {
|
||||
showValidationSpinner(false);
|
||||
if (validRows.length === 0) {
|
||||
document.getElementById("exportResponseMessage").innerHTML =
|
||||
`Nessuna riga valida per l'esportazione.<br>` +
|
||||
`<strong>${invalidCount}</strong> righe con errori di validazione.`;
|
||||
document.getElementById("exportResponseModalLabel").textContent =
|
||||
"Validazione Fallita";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
return;
|
||||
}
|
||||
|
||||
const confirmModal = new bootstrap.Modal(
|
||||
document.getElementById("exportBatchConfirmModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
|
||||
let countText = String(validRows.length);
|
||||
if (invalidCount > 0) {
|
||||
countText += ` (${invalidCount} escluse per errori di validazione)`;
|
||||
}
|
||||
document.getElementById("exportBatchCount").textContent = countText;
|
||||
confirmModal.show();
|
||||
|
||||
const confirmBtn = document.getElementById("exportBatchConfirmBtn");
|
||||
if (pendingBatchConfirmHandler) {
|
||||
confirmBtn.removeEventListener("click", pendingBatchConfirmHandler);
|
||||
}
|
||||
pendingBatchConfirmHandler = () => {
|
||||
pendingBatchConfirmHandler = null;
|
||||
confirmModal.hide();
|
||||
startBatchExport(validRows);
|
||||
};
|
||||
confirmBtn.addEventListener("click", pendingBatchConfirmHandler, { once: true });
|
||||
})
|
||||
.catch((error) => {
|
||||
showValidationSpinner(false);
|
||||
console.error("Batch validation error:", error);
|
||||
document.getElementById("exportResponseMessage").textContent =
|
||||
"Errore di validazione: " + error.message;
|
||||
document.getElementById("exportResponseModalLabel").textContent =
|
||||
"Errore Validazione";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
});
|
||||
}
|
||||
|
||||
exportAllBtn.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
if (batchRunning) return;
|
||||
|
||||
// Check unsaved changes first
|
||||
if (hasUnsavedChanges()) {
|
||||
const unsavedModal = new bootstrap.Modal(
|
||||
document.getElementById("exportBatchUnsavedModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
unsavedModal.show();
|
||||
|
||||
document
|
||||
.getElementById("batchSaveAndExportBtn")
|
||||
.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
unsavedModal.hide();
|
||||
// Trigger Save All, then proceed
|
||||
const saveAllEl =
|
||||
document.querySelector(".save-all-btn");
|
||||
if (!saveAllEl) return;
|
||||
|
||||
// Watch for all .cell-changed to disappear
|
||||
const observer = new MutationObserver(() => {
|
||||
if (!hasUnsavedChanges()) {
|
||||
observer.disconnect();
|
||||
const eligibleRows = collectEligibleRows();
|
||||
if (eligibleRows.length === 0) {
|
||||
document.getElementById(
|
||||
"exportResponseMessage",
|
||||
).textContent =
|
||||
"Tutte le righe sono già state esportate al LIMS.";
|
||||
document.getElementById(
|
||||
"exportResponseModalLabel",
|
||||
).textContent = "Export All";
|
||||
new bootstrap.Modal(
|
||||
document.getElementById(
|
||||
"exportResponseModal",
|
||||
),
|
||||
{ keyboard: false },
|
||||
).show();
|
||||
return;
|
||||
}
|
||||
showBatchConfirm(eligibleRows);
|
||||
}
|
||||
});
|
||||
observer.observe(
|
||||
document.querySelector(".grid-container"),
|
||||
{
|
||||
subtree: true,
|
||||
attributes: true,
|
||||
attributeFilter: ["class"],
|
||||
},
|
||||
);
|
||||
|
||||
saveAllEl.click();
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const eligibleRows = collectEligibleRows();
|
||||
|
||||
if (eligibleRows.length === 0) {
|
||||
document.getElementById("exportResponseMessage").textContent =
|
||||
"Tutte le righe sono già state esportate al LIMS.";
|
||||
document.getElementById("exportResponseModalLabel").textContent =
|
||||
"Export All";
|
||||
const modal = new bootstrap.Modal(
|
||||
document.getElementById("exportResponseModal"),
|
||||
{ keyboard: false },
|
||||
);
|
||||
modal.show();
|
||||
return;
|
||||
}
|
||||
|
||||
showBatchConfirm(eligibleRows);
|
||||
});
|
||||
|
||||
function startBatchExport(eligibleRows) {
|
||||
batchCancelled = false;
|
||||
batchRunning = true;
|
||||
// Don't clear validation errors — they should stay visible for invalid rows
|
||||
const batchUuid = crypto.randomUUID();
|
||||
const total = eligibleRows.length;
|
||||
let processed = 0;
|
||||
let succeeded = 0;
|
||||
let failed = 0;
|
||||
|
||||
// Disable all row buttons
|
||||
disableAllRowButtons();
|
||||
|
||||
// Show inline status bar
|
||||
const bar = document.getElementById("batchExportBar");
|
||||
const statusEl = document.getElementById("batchExportStatus");
|
||||
const cancelBtn = document.getElementById("exportBatchCancelBtn");
|
||||
bar.style.display = "";
|
||||
cancelBtn.disabled = false;
|
||||
statusEl.textContent = `Esportazione 0 / ${total}...`;
|
||||
|
||||
// Cancel handler
|
||||
cancelBtn.addEventListener(
|
||||
"click",
|
||||
() => {
|
||||
batchCancelled = true;
|
||||
statusEl.textContent =
|
||||
"Annullamento... (attendi riga corrente)";
|
||||
cancelBtn.disabled = true;
|
||||
},
|
||||
{ once: true },
|
||||
);
|
||||
|
||||
(async () => {
|
||||
for (let i = 0; i < eligibleRows.length; i++) {
|
||||
if (batchCancelled) break;
|
||||
|
||||
const { iddatadb, row } = eligibleRows[i];
|
||||
statusEl.textContent = `Esportazione ${processed + 1} / ${total} (id: ${iddatadb})...`;
|
||||
|
||||
// Highlight current row
|
||||
setRowExporting(row, true);
|
||||
|
||||
try {
|
||||
const data = await sendExport(iddatadb, row, batchUuid);
|
||||
processed++;
|
||||
if (data.success) {
|
||||
succeeded++;
|
||||
} else {
|
||||
failed++;
|
||||
showRowError(row, iddatadb, data.message || "Errore sconosciuto");
|
||||
}
|
||||
} catch (error) {
|
||||
processed++;
|
||||
failed++;
|
||||
showRowError(row, iddatadb, error.message);
|
||||
}
|
||||
|
||||
// Remove highlight from current row
|
||||
setRowExporting(row, false);
|
||||
}
|
||||
|
||||
// Finished
|
||||
batchRunning = false;
|
||||
enableAllRowButtons();
|
||||
bar.style.display = "none";
|
||||
|
||||
// Show result modal
|
||||
const msgEl = document.getElementById("exportResponseMessage");
|
||||
const labelEl = document.getElementById("exportResponseModalLabel");
|
||||
|
||||
if (batchCancelled) {
|
||||
labelEl.textContent = "Export All — Annullato";
|
||||
msgEl.innerHTML =
|
||||
`Esportate: <strong>${succeeded}</strong><br>` +
|
||||
`Errori: <strong>${failed}</strong><br>` +
|
||||
`Non processate: <strong>${total - processed}</strong>`;
|
||||
} else if (failed === 0) {
|
||||
labelEl.textContent = "Export All — Completato";
|
||||
msgEl.innerHTML = `Tutte le <strong>${succeeded}</strong> righe esportate con successo.`;
|
||||
} else {
|
||||
labelEl.textContent = "Export All — Completato con errori";
|
||||
msgEl.innerHTML =
|
||||
`Esportate: <strong>${succeeded}</strong><br>` +
|
||||
`Errori: <strong>${failed}</strong>`;
|
||||
}
|
||||
|
||||
const modalEl = document.getElementById("exportResponseModal");
|
||||
const modal = new bootstrap.Modal(modalEl, { keyboard: false });
|
||||
modal.show();
|
||||
modalEl.addEventListener("hidden.bs.modal", cleanupBackdrop, { once: true });
|
||||
})();
|
||||
}
|
||||
});
|
||||
@@ -1,817 +0,0 @@
|
||||
<?php
|
||||
require_once "class/VisualLimsApiClient.class.php";
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
header("Content-Type: application/json");
|
||||
|
||||
// 🔹 Configura directory log (creala se non esiste)
|
||||
$logDir = __DIR__ . '/logs/api/';
|
||||
if (!is_dir($logDir)) {
|
||||
mkdir($logDir, 0755, true);
|
||||
}
|
||||
|
||||
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||
|
||||
/**
|
||||
* Resolve a datadb_photos.file_path to an absolute file path on disk.
|
||||
*/
|
||||
function resolvePhotoFullPath(string $filePath, string $uploadDir, string $userareaDir): ?string
|
||||
{
|
||||
$filePath = trim($filePath);
|
||||
if ($filePath === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$base = rtrim($uploadDir, '/');
|
||||
$candidates = [];
|
||||
|
||||
// Userarea-relative form: "../photostrf/annotated/x.png" → resolve from __DIR__.
|
||||
if (strncmp($filePath, '../', 3) === 0) {
|
||||
$candidates[] = rtrim($userareaDir, '/') . '/' . $filePath;
|
||||
}
|
||||
|
||||
// Bare filename or "annotated/x.png" subpath - resolve from photostrf/.
|
||||
$candidates[] = $base . '/' . ltrim($filePath, './');
|
||||
|
||||
// Safety net for any odd/legacy value.
|
||||
$candidates[] = $base . '/' . basename($filePath);
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
$real = realpath($candidate);
|
||||
if ($real && file_exists($real)) {
|
||||
return $real;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 🔹 Base URL API
|
||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
// 🔹 Batch UUID — if present, all logs go to a single file
|
||||
$batchUuid = $_POST['batch_uuid'] ?? null;
|
||||
|
||||
$writeLog = (function () use ($batchUuid, $logDir) {
|
||||
$batchLogFile = $batchUuid ? $logDir . "batch_export_{$batchUuid}.log" : null;
|
||||
|
||||
return function ($individualPath, $content, $stepLabel = null) use ($batchLogFile) {
|
||||
if ($batchLogFile) {
|
||||
$header = "\n" . str_repeat("=", 60) . "\n";
|
||||
if ($stepLabel) {
|
||||
$header .= "[{$stepLabel}] " . date('Y-m-d H:i:s') . "\n";
|
||||
}
|
||||
$header .= str_repeat("=", 60) . "\n";
|
||||
file_put_contents($batchLogFile, $header . $content . "\n", FILE_APPEND);
|
||||
} else {
|
||||
file_put_contents($individualPath, $content);
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
// 🔹 Funzione per validare e convertire date
|
||||
function validateDate($value)
|
||||
{
|
||||
// Prova a validare come data (accetta formati comuni)
|
||||
$date = DateTime::createFromFormat('Y-m-d', $value) ?: DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||
if ($date) {
|
||||
return $date->format('Y-m-d\TH:i:sP'); // Formato ISO 8601
|
||||
}
|
||||
return null; // Imposta null se non è una data valida
|
||||
}
|
||||
|
||||
// 🔹 Funzione per validare e convertire date
|
||||
function formatDateToExport($value)
|
||||
{
|
||||
$date = DateTime::createFromFormat('Y-m-d', $value) ?: DateTime::createFromFormat('Y-m-d H:i:s', $value);
|
||||
if ($date) {
|
||||
return $date->format('d/m/Y');
|
||||
}
|
||||
return null; // Imposta null se non è una data valida
|
||||
}
|
||||
|
||||
// ImportaCommessa con retry: la chiamata è asincrona lato LIMS e a volte
|
||||
// risponde 200 senza importare (StatoCommessaWeb resta "Inviata"/"Nuova").
|
||||
// Riprova con backoff esponenziale finché non passa a "Elaborata".
|
||||
function importaCommessaWithRetry($api, $commessaId, array $payload, $maxRetries = 3, $initialBackoff = 1)
|
||||
{
|
||||
$result = null;
|
||||
$stato = null;
|
||||
$succeeded = false;
|
||||
$log = "";
|
||||
$backoff = $initialBackoff;
|
||||
|
||||
set_time_limit(120); // i backoff non devono far scadere il timeout della richiesta
|
||||
|
||||
for ($attempt = 1; $attempt <= $maxRetries + 1; $attempt++) {
|
||||
try {
|
||||
$result = $api->post("CommessaWeb({$commessaId})/ImportaCommessa", $payload);
|
||||
$stato = $result['StatoCommessaWeb'] ?? null;
|
||||
$log .= "Attempt {$attempt}: HTTP 200, StatoCommessaWeb=" . ($stato ?? 'null') . "\n";
|
||||
} catch (Exception $e) {
|
||||
$stato = null;
|
||||
$log .= "Attempt {$attempt}: EXCEPTION " . $e->getMessage() . "\n";
|
||||
}
|
||||
|
||||
if ($stato === 'Elaborata') {
|
||||
$succeeded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($attempt <= $maxRetries) {
|
||||
$log .= " -> not Elaborata, waiting {$backoff}s before retry\n";
|
||||
sleep($backoff);
|
||||
$backoff *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
return [
|
||||
'succeeded' => $succeeded,
|
||||
'stato' => $stato,
|
||||
'result' => $result,
|
||||
'log' => $log,
|
||||
];
|
||||
}
|
||||
|
||||
try {
|
||||
$iddatadb = $_POST['iddatadb'] ?? null;
|
||||
if (!$iddatadb) {
|
||||
throw new Exception("Missing iddatadb");
|
||||
}
|
||||
|
||||
// TEMP: simulate error on every other row for testing
|
||||
if (env('SIMULATE_EXPORT_LIMS') && $iddatadb % 2 === 0) {
|
||||
throw new Exception("Simulated error for iddatadb $iddatadb");
|
||||
}
|
||||
|
||||
// 🔹 STEP 1+2: Fetch Cliente ID from datadb and Schema ID from excel_templates
|
||||
// Also fetch fixed fields stored in datadb
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId, et.id AS templateId,
|
||||
et.template_note,
|
||||
d.cliente_responsabile_id,
|
||||
d.moltiplicatore_prezzo_id,
|
||||
d.anagrafica_certest_object_id,
|
||||
d.anagrafica_certest_service_id,
|
||||
d.cliente_fornitore_id,
|
||||
d.clienteAnalisi,
|
||||
d.ClienteFatturazione,
|
||||
d.consegna_richiesta
|
||||
FROM datadb as d
|
||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||
WHERE d.iddatadb = :iddatadb
|
||||
LIMIT 1
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$result = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$result) {
|
||||
throw new Exception("No Cliente/Schema found for iddatadb {$iddatadb}");
|
||||
}
|
||||
|
||||
$clienteId = (int) $result['clienteId'];
|
||||
$schemaId = (int) $result['schemaId'];
|
||||
$templateId = (int) $result['templateId'];
|
||||
|
||||
// Extract fixed fields (nullable INTs)
|
||||
$clienteResponsabile = !empty($result['cliente_responsabile_id']) ? (int) $result['cliente_responsabile_id'] : null;
|
||||
$moltiplicatorePrezzo = !empty($result['moltiplicatore_prezzo_id']) ? (int) $result['moltiplicatore_prezzo_id'] : null;
|
||||
$anagraficaObject = !empty($result['anagrafica_certest_object_id']) ? (int) $result['anagrafica_certest_object_id'] : null;
|
||||
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
|
||||
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
||||
$clienteAnalisi = !empty($result['clienteAnalisi']) ? (int) $result['clienteAnalisi'] : null;
|
||||
$clienteFatturazione = !empty($result['ClienteFatturazione']) ? (int) $result['ClienteFatturazione'] : null;
|
||||
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||
|
||||
// 🔹 STEP 3: Fetch Parts (including idmatrice and part id for custom fields)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id AS part_id, part_number, part_description, material, color, mix, idmatrice, dateexpiry, note
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
AND part_description IS NOT NULL
|
||||
AND TRIM(part_description) <> ''
|
||||
ORDER BY CAST(part_number AS UNSIGNED) ASC, part_number ASC
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// 🔹 STEP 3.1: Fetch custom field values per part from identification_parts_customfields
|
||||
$partIds = array_column($parts, 'part_id');
|
||||
$partsCustomFields = []; // part_id => [ { field_id, value_id, value_text }, ... ]
|
||||
if (!empty($partIds)) {
|
||||
$placeholders = implode(',', array_fill(0, count($partIds), '?'));
|
||||
$cfStmt = $pdo->prepare("
|
||||
SELECT part_id, field_id, value_id, value_text
|
||||
FROM identification_parts_customfields
|
||||
WHERE part_id IN ({$placeholders})
|
||||
");
|
||||
$cfStmt->execute($partIds);
|
||||
foreach ($cfStmt->fetchAll(PDO::FETCH_ASSOC) as $cfRow) {
|
||||
$partsCustomFields[(int)$cfRow['part_id']][] = $cfRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE import_data_details idd
|
||||
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||
SET idd.field_value = CASE m.auto_value
|
||||
WHEN 'export_date' THEN :export_date
|
||||
WHEN 'export_time' THEN :export_time
|
||||
END
|
||||
WHERE idd.id = :iddatadb
|
||||
AND m.auto_value IN ('export_date', 'export_time')
|
||||
");
|
||||
$stmt->execute([
|
||||
'iddatadb' => $iddatadb,
|
||||
'export_date' => date('Y-m-d'),
|
||||
'export_time' => date('H:i'),
|
||||
]);
|
||||
|
||||
// 🔹 STEP 4: Fetch Field Values with Labels
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
idd.field_value,
|
||||
m.field_label,
|
||||
m.schema_id,
|
||||
m.field_id,
|
||||
m.field_order,
|
||||
m.is_addnote
|
||||
FROM
|
||||
import_data_details as idd
|
||||
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||
WHERE idd.id = :iddatadb
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$fieldValues = [];
|
||||
$valueMap = [];
|
||||
foreach ($rows as $row) {
|
||||
$fieldValues[] = [
|
||||
"IdCommesseCustomFields" => (int) $row['field_id'],
|
||||
"Valore" => $row['field_value'],
|
||||
"FieldLabel" => $row['field_label']
|
||||
];
|
||||
$valueMap[(int) $row['field_id']] = $row['field_value'];
|
||||
}
|
||||
|
||||
// 🔹 Note aggiuntive: custom field flaggati is_addnote e valorizzati.
|
||||
// Valori a livello di commessa → identici su tutte le parti.
|
||||
$addnoteChunks = [];
|
||||
$addnoteRows = array_filter($rows, fn($r) => (int)($r['is_addnote'] ?? 0) === 1);
|
||||
|
||||
usort($addnoteRows, function ($a, $b) {
|
||||
$oa = (int)($a['field_order'] ?? 9999);
|
||||
$ob = (int)($b['field_order'] ?? 9999);
|
||||
if ($oa !== $ob) return $oa <=> $ob;
|
||||
return strcmp((string)($a['field_label'] ?? ''), (string)($b['field_label'] ?? ''));
|
||||
});
|
||||
|
||||
foreach ($addnoteRows as $r) {
|
||||
$val = trim((string)($r['field_value'] ?? ''));
|
||||
if ($val !== '') {
|
||||
$addnoteChunks[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$addnoteText = implode(' - ', $addnoteChunks);
|
||||
|
||||
// Nota del template: appesa in fondo alle note in esportazione
|
||||
$templateNote = trim((string)($result['template_note'] ?? ''));
|
||||
if ($templateNote !== '') {
|
||||
$addnoteText = $addnoteText !== ''
|
||||
? $addnoteText . ' - ' . $templateNote
|
||||
: $templateNote;
|
||||
}
|
||||
|
||||
// Logga i fieldValues in error_log
|
||||
$logFieldValues = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT);
|
||||
error_log($logFieldValues);
|
||||
|
||||
// 🔹 Initialize API client
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// 🔹 STEP 5: Create CommessaWeb
|
||||
// Fixed fields are sent as direct properties — they are navigation properties on Commessa
|
||||
// accepted by the XAF API even though not explicitly listed in OData $metadata
|
||||
$commessaWebPayload = [
|
||||
"Cliente" => $clienteId,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"Richiedente" => "From TRFSmart Application", // TODO: replace with real value
|
||||
"Descrizione" => "From TRFSmart Application", // TODO: replace with real value
|
||||
"ClienteResponsabile" => $clienteResponsabile,
|
||||
"MoltiplicatorePrezzo" => $moltiplicatorePrezzo,
|
||||
"AnagraficaCertestObject" => $anagraficaObject,
|
||||
"AnagraficaCertestService" => $anagraficaService,
|
||||
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
|
||||
// "ClienteFatturazione" => $clienteFatturazione, // ⚠️ TEMP DISABLED - problemi API lato LIMS
|
||||
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||
];
|
||||
|
||||
// Costruisci log curl-like per STEP 5
|
||||
$jsonPayload = json_encode($commessaWebPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$logContentStep5 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$jsonPayload}'";
|
||||
|
||||
$commessaWeb = $api->post("CommessaWeb", $commessaWebPayload);
|
||||
|
||||
$logContentStep5 .= "\n\nRESPONSE:\n" . json_encode($commessaWeb, JSON_PRETTY_PRINT);
|
||||
|
||||
// Salva log
|
||||
$logFileStep5 = $logDir . "commessa_create_step5_" . $iddatadb . "_" . time() . ".txt";
|
||||
$writeLog($logFileStep5, $logContentStep5, "STEP 5 - Create CommessaWeb (iddatadb={$iddatadb})");
|
||||
|
||||
$commessaId = $commessaWeb["IdCommessa"];
|
||||
$commessaWebCode = substr($commessaWeb["CodiceCommessa"] ?? "TEST CommessaWeb", 0, 30); // Limite a 30 caratteri
|
||||
|
||||
// 🔹 STEP 6: Create Campioni (Samples) for each part
|
||||
$campioni = [];
|
||||
$logContentStep6 = "";
|
||||
|
||||
foreach ($parts as $index => $part) {
|
||||
$matriceId = (int) ($part["idmatrice"] ?? 0);
|
||||
|
||||
if ($matriceId <= 0) {
|
||||
throw new Exception("Invalid or missing idmatrice for part: " . ($part["part_number"] ?? "Unknown"));
|
||||
}
|
||||
|
||||
$partDescription = $part["part_description"] ?? "";
|
||||
$partNote = trim((string)($part["note"] ?? ""));
|
||||
|
||||
// Note aggiuntive dai custom field flaggati is_addnote
|
||||
$partDescription = $part["part_description"] ?? "";
|
||||
$partNote = trim((string)($part["note"] ?? ""));
|
||||
|
||||
// Componi NoteWeb: descrizione [- note] [- addnote di commessa]
|
||||
$noteParts = [];
|
||||
if ($partDescription !== "") {
|
||||
$noteParts[] = $partDescription;
|
||||
}
|
||||
if ($partNote !== "") {
|
||||
$noteParts[] = $partNote;
|
||||
}
|
||||
if ($addnoteText !== "") {
|
||||
$noteParts[] = $addnoteText;
|
||||
}
|
||||
$noteWebValue = implode(" - ", $noteParts);
|
||||
|
||||
$campionePayload = [
|
||||
"Commessa" => $commessaId,
|
||||
"Matrice" => $matriceId,
|
||||
"SottoMatrice" => null,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
// "Riferimento" => $part["part_description"] ?? "",
|
||||
"NoteWeb" => $noteWebValue,
|
||||
"ConsegnaRichiesta" => !empty($part["dateexpiry"]) ? $part["dateexpiry"] : $consegnaRichiesta,
|
||||
];
|
||||
|
||||
// Costruisci curl-like per questo campione
|
||||
$jsonPayload = json_encode($campionePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$logContentStep6 .= "CAMPIONE #{$index}\n" .
|
||||
"curl --location --request POST '{$apiBaseUrl}Campione' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$jsonPayload}'\n\n";
|
||||
|
||||
$campione = $api->post("Campione", $campionePayload);
|
||||
|
||||
$logContentStep6 .= "RESPONSE:\n" . json_encode($campione, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||
|
||||
$campione["PartNumber"] = $part["part_number"] ?? "";
|
||||
$campione["Material"] = $part["material"] ?? "";
|
||||
$campione["Color"] = $part["color"] ?? "";
|
||||
$campione["Mix"] = $part["mix"] ?? "";
|
||||
|
||||
$campioni[] = $campione;
|
||||
}
|
||||
|
||||
// Salva log per STEP 6
|
||||
$logFileStep6 = $logDir . "commessa_{$commessaId}_campioni_step6_" . time() . ".txt";
|
||||
$writeLog($logFileStep6, $logContentStep6, "STEP 6 - Campioni (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 6.0: PATCH each Campione custom fields:
|
||||
// - field 189 (Tested Component) = part_description
|
||||
// - additional fields from identification_parts_customfields (field_id → value_id/value_text)
|
||||
$logContentStep63 = "";
|
||||
foreach ($campioni as $index => $campione) {
|
||||
$campioneId = (int)($campione['IdCampione'] ?? 0);
|
||||
$partDescription = $parts[$index]['part_description'] ?? '';
|
||||
$partId = (int)($parts[$index]['part_id'] ?? 0);
|
||||
|
||||
if ($campioneId <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Build a map of overrides: IdCustomField => value
|
||||
$overrides = [];
|
||||
|
||||
// Override 1: Tested Component (field 189) = part_description
|
||||
if ($partDescription !== '') {
|
||||
$overrides[189] = $partDescription;
|
||||
}
|
||||
|
||||
// Override 2: values from identification_parts_customfields
|
||||
$partCFs = $partsCustomFields[$partId] ?? [];
|
||||
foreach ($partCFs as $pcf) {
|
||||
$cfFieldId = (int)$pcf['field_id'];
|
||||
$cfValue = $pcf['value_text'] ?? $pcf['value_id'] ?? null;
|
||||
if ($cfFieldId > 0 && $cfValue !== null && $cfValue !== '') {
|
||||
$overrides[$cfFieldId] = (string)$cfValue;
|
||||
}
|
||||
}
|
||||
|
||||
// Skip if nothing to override
|
||||
if (empty($overrides)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// GET campione custom fields
|
||||
$campioneWithFields = $api->get("Campione({$campioneId})?\$expand=CampioniCustomFields(\$expand=CustomField)");
|
||||
|
||||
$logContentStep63 .= "GET Campione({$campioneId}) CustomFields:\n" .
|
||||
json_encode($campioneWithFields['CampioniCustomFields'] ?? [], JSON_PRETTY_PRINT) . "\n\n";
|
||||
$logContentStep63 .= "Overrides for part {$partId}: " . json_encode($overrides) . "\n\n";
|
||||
|
||||
// Build PATCH payload — apply overrides where IdCustomField matches
|
||||
$campioniCustomFields = [];
|
||||
foreach ($campioneWithFields["CampioniCustomFields"] ?? [] as $cf) {
|
||||
$definitionId = (int)($cf["CustomField"]["IdCustomField"] ?? 0);
|
||||
$fieldInstanceId = (int)$cf["IdCampioniCustomFields"];
|
||||
$currentValue = $cf["Valore"] ?? '';
|
||||
|
||||
$newValue = $overrides[$definitionId] ?? $currentValue;
|
||||
|
||||
$campioniCustomFields[] = [
|
||||
"IdCampioniCustomFields" => $fieldInstanceId,
|
||||
"Valore" => $newValue
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($campioniCustomFields)) {
|
||||
$patchPayload = ["CampioniCustomFields" => $campioniCustomFields];
|
||||
$patchJson = json_encode($patchPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$logContentStep63 .= "PATCH Campione({$campioneId}):\n" .
|
||||
"curl --location --request PATCH '{$apiBaseUrl}Campione({$campioneId})' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$patchJson}'\n\n";
|
||||
|
||||
$patchResult = $api->patch("Campione({$campioneId})", $patchPayload);
|
||||
|
||||
$logContentStep63 .= "RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n---\n";
|
||||
}
|
||||
}
|
||||
|
||||
$logFileStep63 = $logDir . "commessa_{$commessaId}_campioni_customfields_step60_" . time() . ".txt";
|
||||
$writeLog($logFileStep63, $logContentStep63, "STEP 6.0 - Campioni CustomFields (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 6.1: Fetch photos linked to this iddatadb
|
||||
$stmtPhotos = $pdo->prepare("
|
||||
SELECT id, file_path, file_name, StampaNelRapporto, PrimaPagina
|
||||
FROM datadb_photos
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY id ASC
|
||||
");
|
||||
$stmtPhotos->execute(['iddatadb' => $iddatadb]);
|
||||
$photos = $stmtPhotos->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
|
||||
// 🔹 STEP 6.2: Upload photos to Campione .01 (fetched from API)
|
||||
$photosUploaded = 0;
|
||||
$logContentPhotos = "Photos for CommessaWeb {$commessaId} (iddatadb={$iddatadb}):\n";
|
||||
$logContentPhotos .= "Total photos found: " . count($photos) . ", campioni: " . count($campioni) . "\n\n";
|
||||
|
||||
if (!empty($campioni) && !empty($photos)) {
|
||||
// Fetch campioni list from API to find the .01 campione
|
||||
$commessaCampioni = $api->get("CommessaWeb({$commessaId})?\$expand=Campioni");
|
||||
$apiCampioni = $commessaCampioni['Campioni'] ?? [];
|
||||
|
||||
// Sort by CodiceCampione to find .01
|
||||
usort($apiCampioni, fn($a, $b) => strcmp($a['CodiceCampione'] ?? '', $b['CodiceCampione'] ?? ''));
|
||||
|
||||
$mainCampione = $apiCampioni[0] ?? null;
|
||||
$campioneId = (int)($mainCampione['IdCampione'] ?? 0);
|
||||
|
||||
$logContentPhotos .= "API Campioni order:\n";
|
||||
foreach ($apiCampioni as $ac) {
|
||||
$logContentPhotos .= " - {$ac['CodiceCampione']} (IdCampione: {$ac['IdCampione']})\n";
|
||||
}
|
||||
$logContentPhotos .= "Selected .01 campione: IdCampione={$campioneId}\n\n";
|
||||
|
||||
if ($campioneId > 0) {
|
||||
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||
|
||||
foreach ($photos as $photo) {
|
||||
$fullPath = resolvePhotoFullPath($photo['file_path'], $uploadDir, __DIR__);
|
||||
|
||||
if ($fullPath === null) {
|
||||
$logContentPhotos .= "SKIP (file not found): file_path='{$photo['file_path']}'\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$photoEndpoint = "Campione({$campioneId})/UploadCampioneFile";
|
||||
$stampaNelRapporto = !empty($photo['StampaNelRapporto']);
|
||||
$primaPagina = !empty($photo['PrimaPagina']);
|
||||
|
||||
$logContentPhotos .= "curl --location --request POST '{$apiBaseUrl}{$photoEndpoint}' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--form 'file=@{$fullPath}'\n\n";
|
||||
|
||||
// Step 1: Upload file (flags are ignored by API during upload)
|
||||
$photoResult = $api->postMultipart($photoEndpoint, $fullPath, $photo['file_name']);
|
||||
$logContentPhotos .= "UPLOAD RESPONSE:\n" . json_encode($photoResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||
|
||||
// Step 2: PATCH CampioneFile to set flags (StampaNelRapporto, PrimaPagina)
|
||||
$campioneFileId = (int)($photoResult['IdCampioneFile'] ?? 0);
|
||||
if ($campioneFileId > 0 && ($stampaNelRapporto || $primaPagina)) {
|
||||
$patchPayload = [];
|
||||
|
||||
if ($stampaNelRapporto) {
|
||||
$patchPayload['StampaNelRapporto'] = true;
|
||||
}
|
||||
|
||||
if ($primaPagina) {
|
||||
$patchPayload['PrimaPagina'] = true;
|
||||
}
|
||||
|
||||
$patchEndpoint = "CampioneFile({$campioneFileId})";
|
||||
$patchJsonLog = json_encode($patchPayload, JSON_PRETTY_PRINT);
|
||||
$logContentPhotos .= "curl --location --request PATCH '{$apiBaseUrl}{$patchEndpoint}' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$patchJsonLog}'\n\n";
|
||||
|
||||
$patchResult = $api->patch($patchEndpoint, $patchPayload);
|
||||
$logContentPhotos .= "PATCH RESPONSE:\n" . json_encode($patchResult, JSON_PRETTY_PRINT) . "\n\n";
|
||||
}
|
||||
|
||||
$logContentPhotos .= "---\n";
|
||||
$photosUploaded++;
|
||||
}
|
||||
} else {
|
||||
$logContentPhotos .= "SKIP: main campione has invalid IdCampione\n";
|
||||
}
|
||||
} elseif (empty($campioni)) {
|
||||
$logContentPhotos .= "SKIP: no campioni created, cannot upload photos\n";
|
||||
}
|
||||
|
||||
$logFilePhotos = $logDir . "commessa_{$commessaId}_photos_step5_2_" . time() . ".txt";
|
||||
$writeLog($logFilePhotos, $logContentPhotos, "STEP 6.2 - Photos (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 6.3: Add Analyses (AnalisiCampione) via Campione({id})/AddAnalisi bound action
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT part_id, analysis_recordkey, analysis_name, analysis_method
|
||||
FROM identification_parts_analyses
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY part_id, id
|
||||
");
|
||||
$stmt->execute(['iddatadb' => $iddatadb]);
|
||||
$analysesRows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$partIdToIndex = [];
|
||||
foreach ($parts as $idx => $part) {
|
||||
$partIdToIndex[(int)$part['part_id']] = $idx;
|
||||
}
|
||||
|
||||
$totalAnalyses = count($analysesRows);
|
||||
$addedAnalyses = 0;
|
||||
$failedAnalyses = [];
|
||||
$logContentStep63Analisi = "Analyses for iddatadb={$iddatadb}: total={$totalAnalyses}\n\n";
|
||||
|
||||
foreach ($analysesRows as $a) {
|
||||
$partId = (int)$a['part_id'];
|
||||
$recordKey = trim((string)($a['analysis_recordkey'] ?? ''));
|
||||
$idx = $partIdToIndex[$partId] ?? null;
|
||||
|
||||
if ($idx === null || !isset($campioni[$idx]) || $recordKey === '') {
|
||||
$logContentStep63Analisi .= "SKIP (no campione for part_id={$partId} / empty recordkey): '{$recordKey}'\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$campioneId = (int)($campioni[$idx]['IdCampione'] ?? 0);
|
||||
if ($campioneId <= 0) {
|
||||
$logContentStep63Analisi .= "SKIP (invalid IdCampione for part_id={$partId}): '{$recordKey}'\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
$payload = ['RecordKey' => $recordKey];
|
||||
$jsonPayload = json_encode($payload, JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$logContentStep63Analisi .= "curl --location --request POST '{$apiBaseUrl}Campione({$campioneId})/AddAnalisi' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$jsonPayload}'\n";
|
||||
|
||||
try {
|
||||
$result = $api->post("Campione({$campioneId})/AddAnalisi", $payload);
|
||||
$logContentStep63Analisi .= "OK (part_id={$partId}, campione={$campioneId}): " .
|
||||
($a['analysis_name'] ?? '') . "\n---\n";
|
||||
$addedAnalyses++;
|
||||
} catch (Exception $e) {
|
||||
$errMsg = $e->getMessage();
|
||||
$logContentStep63Analisi .= "FAIL: {$errMsg}\n---\n";
|
||||
$failedAnalyses[] = [
|
||||
'part_id' => $partId,
|
||||
'campione_id' => $campioneId,
|
||||
'analysis_recordkey' => $recordKey,
|
||||
'analysis_name' => $a['analysis_name'] ?? '',
|
||||
'error' => $errMsg,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
$logFileStep63Analisi = $logDir . "commessa_{$commessaId}_analyses_step63_" . time() . ".txt";
|
||||
$writeLog($logFileStep63Analisi, $logContentStep63Analisi, "STEP 6.3 - AddAnalisi (commessa={$commessaId})");
|
||||
|
||||
// 🔹 STEP 7: Update Custom Fields for CommessaWeb
|
||||
if (!empty($fieldValues)) {
|
||||
// GET con espansione per CustomField
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
$commessaWithFields = $api->get("CommessaWeb({$commessaId})?\$expand={$expand}");
|
||||
|
||||
// Logga il GET
|
||||
$logContentGet = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaWithFields, JSON_PRETTY_PRINT);
|
||||
$logFileGet = $logDir . "commessa_{$commessaId}_get_step7_" . time() . ".txt";
|
||||
$writeLog($logFileGet, $logContentGet, "STEP 7 - GET CustomFields (commessa={$commessaId})");
|
||||
|
||||
// Prepara payload PATCH
|
||||
$commessaCustomFields = [];
|
||||
foreach ($commessaWithFields["CommesseCustomFields"] as $customField) {
|
||||
$definitionId = (int) ($customField["CustomField"]["IdCustomField"] ?? 0);
|
||||
$fieldId = (int) $customField["IdCommesseCustomFields"];
|
||||
$currentValue = $customField["Valore"] ?? '';
|
||||
$fieldType = $customField["CustomField"]["Tipo"] ?? '';
|
||||
$newValue = isset($valueMap[$definitionId]) ? $valueMap[$definitionId] : $currentValue;
|
||||
|
||||
// Valida se il campo è di tipo Data
|
||||
if ($fieldType === 'Data' && $newValue !== $currentValue) {
|
||||
$newValue = formatDateToExport($newValue);
|
||||
}
|
||||
|
||||
$commessaCustomFields[] = [
|
||||
"IdCommesseCustomFields" => $fieldId,
|
||||
"Valore" => $newValue
|
||||
];
|
||||
}
|
||||
|
||||
$logFileStep7 = null;
|
||||
if (!empty($commessaCustomFields)) {
|
||||
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
|
||||
|
||||
// Logga payload e response
|
||||
$jsonPayload = json_encode($updatePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
$logContentStep7 = "curl --location --request PATCH '{$apiBaseUrl}CommessaWeb({$commessaId})' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$jsonPayload}'";
|
||||
|
||||
$patchResponse = $api->patch("CommessaWeb({$commessaId})", $updatePayload);
|
||||
|
||||
$logContentStep7 .= "\n\nRESPONSE:\n" . json_encode($patchResponse, JSON_PRETTY_PRINT);
|
||||
$logFileStep7 = $logDir . "commessa_{$commessaId}_update_step7_" . time() . ".txt";
|
||||
$writeLog($logFileStep7, $logContentStep7, "STEP 7 - PATCH CustomFields (commessa={$commessaId})");
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, status and export date
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE datadb
|
||||
SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l', export_date = NOW()
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
$stmt->execute([
|
||||
'idcommessaweb' => $commessaId,
|
||||
'commessaweb' => $commessaWebCode,
|
||||
'iddatadb' => $iddatadb
|
||||
]);
|
||||
|
||||
// 🔹 STEP 9: Send CommessaWeb to laboratory (commentato come richiesto)
|
||||
|
||||
$sendResult = $api->post("CommessaWeb({$commessaId})/InviaCommessa", []);
|
||||
|
||||
// Logga il POST
|
||||
$logContentStep9 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/InviaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{}'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($sendResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep9 = $logDir . "commessa_{$commessaId}_send_step9_" . time() . ".txt";
|
||||
$writeLog($logFileStep9, $logContentStep9, "STEP 9 - InviaCommessa (commessa={$commessaId})");
|
||||
|
||||
|
||||
// 🔹 STEP 9.5: Importazione da CommessaWeb a Commessa (con retry)
|
||||
// Supplier call: POST api/odata/CommessaWeb(XXX)/ImportaCommessa
|
||||
$importUserId = (!empty($lims_global_user_id) && is_numeric($lims_global_user_id))
|
||||
? (int) $lims_global_user_id
|
||||
: 285;
|
||||
|
||||
$importPayload = [
|
||||
"IdUtente" => $importUserId
|
||||
];
|
||||
$importPayloadLog = json_encode($importPayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
|
||||
|
||||
$importOutcome = importaCommessaWithRetry($api, $commessaId, $importPayload);
|
||||
$importResult = $importOutcome['result'];
|
||||
$importStato = $importOutcome['stato'];
|
||||
$importSucceeded = $importOutcome['succeeded'];
|
||||
|
||||
// Logga il POST (tutti i tentativi)
|
||||
$logContentStep91 = "curl --location --request POST '{$apiBaseUrl}CommessaWeb({$commessaId})/ImportaCommessa' \\\n" .
|
||||
"--header 'Content-Type: application/json' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••' \\\n" .
|
||||
"--data '{$importPayloadLog}'\n\n" .
|
||||
"ATTEMPTS:\n" . $importOutcome['log'] . "\n" .
|
||||
"SUCCEEDED: " . ($importSucceeded ? 'yes' : 'NO') . "\n\n" .
|
||||
"LAST RESPONSE:\n" . json_encode($importResult, JSON_PRETTY_PRINT);
|
||||
$logFileStep91 = $logDir . "commessa_{$commessaId}_importa_step91_" . time() . ".txt";
|
||||
$writeLog($logFileStep91, $logContentStep91, "STEP 9.5 - ImportaCommessa (commessa={$commessaId}, succeeded=" . ($importSucceeded ? '1' : '0') . ")");
|
||||
|
||||
// 🔹 STEP 10: GET di controllo post-PATCH
|
||||
$expand = "CommesseCustomFields(\$expand=CustomField)";
|
||||
$commessaAfterPatch = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
|
||||
|
||||
// Logga il GET di controllo
|
||||
$logContentStep10 = "curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
|
||||
"--header 'Authorization: Bearer ••••••'\n\n" .
|
||||
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
|
||||
$logFileStep10 = $logDir . "commessa_{$commessaId}_get_step10_" . time() . ".txt";
|
||||
$writeLog($logFileStep10, $logContentStep10, "STEP 10 - GET verify (commessa={$commessaId})");
|
||||
// 🔹 STEP 10.1: Save final CodiceCommessa into datadb.commessaweb
|
||||
// After ImportaCommessa, the API returns the final LIMS job code in CodiceCommessa.
|
||||
// Example: CodiceCommessa = 2614795, CodiceCommessaWeb = 26C0103.
|
||||
$finalCodiceCommessa = trim((string)($commessaAfterPatch['CodiceCommessa'] ?? ''));
|
||||
|
||||
if ($finalCodiceCommessa !== '') {
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE datadb
|
||||
SET commessaweb = :commessaweb,
|
||||
status = 'l'
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
$stmt->execute([
|
||||
'commessaweb' => substr($finalCodiceCommessa, 0, 30),
|
||||
'iddatadb' => $iddatadb
|
||||
]);
|
||||
}
|
||||
// 🔹 STEP 11: Prepare final response
|
||||
$finalCommessa = [
|
||||
"Cliente" => $clienteId,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
"Richiedente" => $commessaWeb["Richiedente"] ?? "Web Import",
|
||||
"Descrizione" => $commessaWeb["Descrizione"] ?? "",
|
||||
"CommesseCustomFields" => $commessaAfterPatch["CommesseCustomFields"] ?? [],
|
||||
"Campioni" => $campioni,
|
||||
"Inviata" => 0 // Non inviato, come richiesto
|
||||
];
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"idcommessaweb" => $commessaId,
|
||||
"commessaweb" => $finalCodiceCommessa ?: $commessaWebCode,
|
||||
"commessaWeb" => $finalCommessa,
|
||||
"commessaWebApiResponse" => $commessaWeb, // Incluso per debug
|
||||
"totalCampioni" => count($campioni),
|
||||
"totalCustomFields" => count($commessaAfterPatch["CommesseCustomFields"] ?? []),
|
||||
"totalPhotos" => count($photos),
|
||||
"totalAnalyses" => $totalAnalyses,
|
||||
"addedAnalyses" => $addedAnalyses,
|
||||
"failedAnalyses" => $failedAnalyses,
|
||||
"message" => "Export successful",
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5,
|
||||
"step5_2_photos" => $logFilePhotos,
|
||||
"step6_campioni" => $logFileStep6,
|
||||
"step63_analyses" => $logFileStep63Analisi,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step9_1_importa" => $logFileStep91,
|
||||
"step10_get" => $logFileStep10
|
||||
]
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
error_log("LIMS Export Error: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
|
||||
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"message" => "Export failed: " . $e->getMessage(),
|
||||
"logFiles" => [
|
||||
"step5_create" => $logFileStep5 ?? null,
|
||||
"step5_2_photos" => $logFilePhotos ?? null,
|
||||
"step6_campioni" => $logFileStep6 ?? null,
|
||||
"step63_analyses" => $logFileStep63Analisi ?? null,
|
||||
"step7_patch" => $logFileStep7 ?? null,
|
||||
"step9_1_importa" => $logFileStep91 ?? null,
|
||||
"step10_get" => $logFileStep10 ?? null
|
||||
]
|
||||
]);
|
||||
}
|
||||
@@ -1,394 +0,0 @@
|
||||
<?php
|
||||
|
||||
include('include/headscript.php');
|
||||
require_once __DIR__ . '/include/grid_data_builder.php';
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
const FILTER_ALLOWED_PER_PAGE = [20, 40, 60, 100];
|
||||
const FILTER_ALLIDS_CAP = 5000;
|
||||
|
||||
$body = json_decode(file_get_contents('php://input'), true) ?: [];
|
||||
|
||||
$templateId = (int)($body['template_id'] ?? 0);
|
||||
if ($templateId <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'template_id mancante']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$status = in_array($body['status'] ?? 'i', ['i', 'P', 'l'], true) ? $body['status'] : 'i';
|
||||
$showAll = !empty($body['all_users']);
|
||||
$importref = trim((string)($body['importref'] ?? '')); // scope su un singolo import
|
||||
$filters = is_array($body['filters'] ?? null) ? $body['filters'] : [];
|
||||
$userId = (int)($iduserlogin ?? 0);
|
||||
$perPage = in_array((int)($body['per_page'] ?? 20), FILTER_ALLOWED_PER_PAGE, true) ? (int)$body['per_page'] : 20;
|
||||
$page = max(1, (int)($body['page'] ?? 1));
|
||||
$wantAllIds = !empty($body['want_all_ids']);
|
||||
|
||||
$cacheDir = __DIR__ . '/cache';
|
||||
|
||||
$explicitIds = [];
|
||||
if (isset($body['ids']) && is_array($body['ids'])) {
|
||||
$explicitIds = array_values(array_unique(array_filter(
|
||||
array_map('intval', $body['ids']),
|
||||
fn($v) => $v > 0
|
||||
)));
|
||||
}
|
||||
if (!empty($explicitIds)) {
|
||||
// Scoping di sicurezza: solo record del template/utente/import corretti.
|
||||
$conds = ['d.templateid = ?', 'd.status = ?'];
|
||||
$params = [$templateId, $status];
|
||||
if (!$showAll) { $conds[] = 'd.user_id = ?'; $params[] = $userId; }
|
||||
if ($importref !== '') { $conds[] = 'd.importreferencecode = ?'; $params[] = $importref; }
|
||||
$ph = implode(',', array_fill(0, count($explicitIds), '?'));
|
||||
$conds[] = "d.iddatadb IN ($ph)";
|
||||
foreach ($explicitIds as $id) $params[] = $id;
|
||||
$whereSql = implode(' AND ', $conds);
|
||||
|
||||
try {
|
||||
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d WHERE {$whereSql}");
|
||||
$countStmt->execute($params);
|
||||
$total = (int)$countStmt->fetchColumn();
|
||||
$totalPages = max(1, (int)ceil($total / $perPage));
|
||||
if ($page > $totalPages) $page = $totalPages;
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$idStmt = $pdo->prepare("
|
||||
SELECT d.iddatadb FROM datadb d
|
||||
WHERE {$whereSql}
|
||||
ORDER BY d.excelrow ASC, d.iddatadb ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
");
|
||||
$idStmt->execute($params);
|
||||
$pageIds = array_map('intval', $idStmt->fetchAll(PDO::FETCH_COLUMN));
|
||||
|
||||
$config = buildGridConfig($pdo, $templateId);
|
||||
$rows = buildGridRows($pdo, $pageIds, $config);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'total_pages' => $totalPages,
|
||||
'rows' => $rows,
|
||||
'mode' => 'selection',
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
error_log('filter_records (ids mode) error: ' . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$fixedAliasMap = [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'ClienteFatturazione' => 'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||
];
|
||||
$staticCols = ['importreferencecode', 'filename_import', 'importdate', 'commessaweb'];
|
||||
|
||||
// ── Risolutori "etichetta → lista di ID" via cache ────────────────────────
|
||||
function cacheItems(string $file): array
|
||||
{
|
||||
if (!is_file($file)) return [];
|
||||
$data = json_decode(file_get_contents($file), true);
|
||||
if (!is_array($data)) return [];
|
||||
return $data['value'] ?? $data ?? [];
|
||||
}
|
||||
|
||||
/** Clienti: label "Nome - X - id" (come search_clienti.php). */
|
||||
function resolveClientIdsByTerm(string $cacheDir, string $term): array
|
||||
{
|
||||
$term = mb_strtolower(trim($term));
|
||||
if ($term === '') return [];
|
||||
$ids = [];
|
||||
foreach (cacheItems($cacheDir . '/clienti.json') as $c) {
|
||||
$name = trim($c['Nominativo'] ?? '');
|
||||
$id = trim((string)($c['IdCliente'] ?? ''));
|
||||
$code = trim((string)($c['CodiceCliente'] ?? ''));
|
||||
$parts = explode('_', $code);
|
||||
$suffix = trim($parts[1] ?? '');
|
||||
if ($suffix === '' && $code !== '') $suffix = substr($code, 0, 1);
|
||||
if ($suffix === '') $suffix = '--';
|
||||
$label = mb_strtolower($name . ' - ' . $suffix . ' - ' . $id);
|
||||
if (mb_strpos($label, $term) !== false) $ids[] = (int)$c['IdCliente'];
|
||||
}
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
/** Dropdown generico: matcha il termine su qualsiasi label field, ritorna gli id. */
|
||||
function resolveIdsFromCache(string $file, string $idField, array $labelFields, string $term): array
|
||||
{
|
||||
$term = mb_strtolower(trim($term));
|
||||
if ($term === '') return [];
|
||||
$ids = [];
|
||||
foreach (cacheItems($file) as $it) {
|
||||
$hay = '';
|
||||
foreach ($labelFields as $lf) $hay .= ' ' . ($it[$lf] ?? '');
|
||||
if (mb_strpos(mb_strtolower($hay), $term) !== false && isset($it[$idField])) {
|
||||
$ids[] = (int)$it[$idField];
|
||||
}
|
||||
}
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
/**
|
||||
* Per un fixed field che memorizza un ID, risolve il termine in lista di id.
|
||||
* Ritorna: array di id (IN), [] se il termine non matcha nulla, null se la colonna
|
||||
* non è risolvibile (→ il chiamante la salta invece di fare LIKE sbagliato).
|
||||
*/
|
||||
function resolveFixedIds(string $cacheDir, string $key, string $term): ?array
|
||||
{
|
||||
switch ($key) {
|
||||
case 'ClienteAnalisi':
|
||||
case 'ClienteFatturazione':
|
||||
return resolveClientIdsByTerm($cacheDir, $term);
|
||||
case 'MoltiplicatorePrezzo':
|
||||
return resolveIdsFromCache($cacheDir . '/moltiplicatori_prezzo.json', 'IdMoltiplicatorePrezzo', ['Codice', 'Descrizione'], $term);
|
||||
case 'AnagraficaCertestObject':
|
||||
return resolveIdsFromCache($cacheDir . '/anagrafica_certest_object.json', 'IdAnagrafica', ['Codice', 'NomeAnagrafica'], $term);
|
||||
case 'AnagraficaCertestService':
|
||||
return resolveIdsFromCache($cacheDir . '/anagrafica_certest_service.json', 'IdAnagrafica', ['Codice', 'NomeAnagrafica'], $term);
|
||||
default:
|
||||
// ClienteResponsabile (per-cliente) e altri: non risolvibili globalmente
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<array{0:int,1:int}> lista di [idclient, idResponsabile]
|
||||
*/
|
||||
function resolveResponsabilePairs(string $cacheDir, string $term): array
|
||||
{
|
||||
$term = mb_strtolower(trim($term));
|
||||
if ($term === '') return [];
|
||||
$pairs = [];
|
||||
foreach (glob($cacheDir . '/cliente_responsabili_*.json') ?: [] as $file) {
|
||||
if (!preg_match('/cliente_responsabili_(\d+)\.json$/', $file, $m)) continue;
|
||||
$cid = (int)$m[1];
|
||||
$data = json_decode(file_get_contents($file), true);
|
||||
foreach (($data['Responsabili'] ?? []) as $r) {
|
||||
$name = mb_strtolower((string)($r['Nominativo'] ?? ''));
|
||||
if ($name !== '' && mb_strpos($name, $term) !== false && isset($r['IdClienteResponsabile'])) {
|
||||
$pairs[] = [$cid, (int)$r['IdClienteResponsabile']];
|
||||
}
|
||||
}
|
||||
}
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
/** Meta di un mapping (data_type, field_id), con cache statica. */
|
||||
function mappingMeta(PDO $pdo, int $mappingId): array
|
||||
{
|
||||
static $cache = [];
|
||||
if (!array_key_exists($mappingId, $cache)) {
|
||||
$st = $pdo->prepare("SELECT data_type, field_id FROM template_mapping WHERE id = ?");
|
||||
$st->execute([$mappingId]);
|
||||
$cache[$mappingId] = $st->fetch(PDO::FETCH_ASSOC) ?: ['data_type' => 'Testo', 'field_id' => null];
|
||||
}
|
||||
return $cache[$mappingId];
|
||||
}
|
||||
|
||||
function resolveCustomFieldValueIds(string $cacheDir, $fieldId, string $term): array
|
||||
{
|
||||
$fieldId = (int)$fieldId;
|
||||
$term = mb_strtolower(trim($term));
|
||||
if ($fieldId <= 0 || $term === '') return [];
|
||||
$ids = [];
|
||||
foreach (cacheItems($cacheDir . "/customfield_{$fieldId}.json") as $it) {
|
||||
$label = mb_strtolower((string)($it['Valore'] ?? ''));
|
||||
if (mb_strpos($label, $term) !== false && isset($it['IdCustomFieldsValue'])) {
|
||||
$ids[] = (int)$it['IdCustomFieldsValue'];
|
||||
}
|
||||
}
|
||||
return array_values(array_unique($ids));
|
||||
}
|
||||
|
||||
// ── Costruzione WHERE ──────────────────────────────────────────────────────
|
||||
$conds = ['d.templateid = ?', 'd.status = ?'];
|
||||
$params = [$templateId, $status];
|
||||
$needUserJoin = false;
|
||||
|
||||
if (!$showAll) {
|
||||
$conds[] = 'd.user_id = ?';
|
||||
$params[] = $userId;
|
||||
}
|
||||
|
||||
// Scope su un singolo import (come imported.php ?importref=)
|
||||
if ($importref !== '') {
|
||||
$conds[] = 'd.importreferencecode = ?';
|
||||
$params[] = $importref;
|
||||
}
|
||||
|
||||
/** Aggiunge una condizione "colonna id IN (lista risolta)". */
|
||||
function addIdInCondition(array &$conds, array &$params, string $col, array $ids): void
|
||||
{
|
||||
if (empty($ids)) {
|
||||
$conds[] = '1 = 0'; // termine dato ma nessun match → zero risultati
|
||||
return;
|
||||
}
|
||||
$ph = implode(',', array_fill(0, count($ids), '?'));
|
||||
$conds[] = "d.`{$col}` IN ($ph)";
|
||||
foreach ($ids as $id) $params[] = (int)$id;
|
||||
}
|
||||
|
||||
foreach ($filters as $f) {
|
||||
$term = trim((string)($f['term'] ?? ''));
|
||||
if ($term === '') continue;
|
||||
$type = (string)($f['type'] ?? '');
|
||||
$key = (string)($f['key'] ?? '');
|
||||
$like = '%' . $term . '%';
|
||||
|
||||
switch ($type) {
|
||||
case 'detail':
|
||||
case 'main_field':
|
||||
$mappingId = (int)$key;
|
||||
if ($mappingId <= 0) break;
|
||||
$mm = mappingMeta($pdo, $mappingId);
|
||||
if (($mm['data_type'] ?? '') === 'SceltaMultipla') {
|
||||
// Dropdown: field_value = ID valore → risolvi label→id, match IN (= non LIKE)
|
||||
$vids = resolveCustomFieldValueIds($cacheDir, $mm['field_id'], $term);
|
||||
if (empty($vids)) {
|
||||
$conds[] = '1 = 0';
|
||||
break;
|
||||
}
|
||||
$inph = implode(',', array_fill(0, count($vids), '?'));
|
||||
$conds[] = "EXISTS (SELECT 1 FROM import_data_details x
|
||||
WHERE x.id = d.iddatadb AND x.mapping_id = ? AND x.field_value IN ($inph))";
|
||||
$params[] = $mappingId;
|
||||
foreach ($vids as $vid) $params[] = $vid;
|
||||
} else {
|
||||
// Testo / Data: match testuale
|
||||
$conds[] = "EXISTS (SELECT 1 FROM import_data_details x
|
||||
WHERE x.id = d.iddatadb AND x.mapping_id = ? AND x.field_value LIKE ?)";
|
||||
$params[] = $mappingId;
|
||||
$params[] = $like;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'idclient':
|
||||
addIdInCondition($conds, $params, 'idclient', resolveClientIdsByTerm($cacheDir, $term));
|
||||
break;
|
||||
|
||||
case 'cliente_fornitore_id':
|
||||
addIdInCondition($conds, $params, 'cliente_fornitore_id', resolveClientIdsByTerm($cacheDir, $term));
|
||||
break;
|
||||
|
||||
case 'tested_component':
|
||||
$conds[] = 'd.tested_component LIKE ?';
|
||||
$params[] = $like;
|
||||
break;
|
||||
|
||||
case 'status':
|
||||
// Il dropdown invia direttamente il codice ('i'/'P'/'l').
|
||||
if (in_array($term, ['i', 'P', 'l'], true)) {
|
||||
$conds[] = 'd.status = ?';
|
||||
$params[] = $term;
|
||||
} else {
|
||||
$conds[] = '1 = 0';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fixed':
|
||||
$col = $fixedAliasMap[$key] ?? null;
|
||||
if ($col === null) break;
|
||||
if ($key === 'ConsegnaRichiesta') {
|
||||
// Data: match testuale sul valore data (es. "2026-03").
|
||||
$conds[] = "CAST(d.`{$col}` AS CHAR) LIKE ?";
|
||||
$params[] = $like;
|
||||
} elseif ($key === 'ClienteResponsabile') {
|
||||
// Match sulla coppia (idclient, id responsabile).
|
||||
$pairs = resolveResponsabilePairs($cacheDir, $term);
|
||||
if (empty($pairs)) {
|
||||
$conds[] = '1 = 0';
|
||||
break;
|
||||
}
|
||||
$rowvals = implode(',', array_fill(0, count($pairs), '(?,?)'));
|
||||
$conds[] = "(d.idclient, d.`{$col}`) IN ($rowvals)";
|
||||
foreach ($pairs as $pr) {
|
||||
$params[] = $pr[0];
|
||||
$params[] = $pr[1];
|
||||
}
|
||||
} else {
|
||||
$ids = resolveFixedIds($cacheDir, $key, $term);
|
||||
if ($ids === null) break; // non risolvibile → non filtrare (niente LIKE su id)
|
||||
addIdInCondition($conds, $params, $col, $ids);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'static':
|
||||
if ($key === 'user_name') {
|
||||
$needUserJoin = true;
|
||||
$conds[] = "CONCAT(COALESCE(u.first_name,''),' ',COALESCE(u.last_name,'')) LIKE ?";
|
||||
$params[] = $like;
|
||||
} elseif (in_array($key, $staticCols, true)) {
|
||||
$conds[] = "d.`{$key}` LIKE ?";
|
||||
$params[] = $like;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break; // tracking/awb ecc. → non filtrabili
|
||||
}
|
||||
}
|
||||
|
||||
$whereSql = implode(' AND ', $conds);
|
||||
$joinSql = $needUserJoin ? 'LEFT JOIN auth_users u ON d.user_id = u.id' : '';
|
||||
|
||||
try {
|
||||
// Totale + pagine
|
||||
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM datadb d {$joinSql} WHERE {$whereSql}");
|
||||
$countStmt->execute($params);
|
||||
$total = (int)$countStmt->fetchColumn();
|
||||
$totalPages = max(1, (int)ceil($total / $perPage));
|
||||
if ($page > $totalPages) $page = $totalPages;
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
// Pagina corrente
|
||||
$pageStmt = $pdo->prepare("
|
||||
SELECT d.iddatadb
|
||||
FROM datadb d {$joinSql}
|
||||
WHERE {$whereSql}
|
||||
ORDER BY d.excelrow ASC, d.iddatadb ASC
|
||||
LIMIT {$perPage} OFFSET {$offset}
|
||||
");
|
||||
$pageStmt->execute($params);
|
||||
$pageIds = array_map('intval', $pageStmt->fetchAll(PDO::FETCH_COLUMN));
|
||||
|
||||
$config = buildGridConfig($pdo, $templateId);
|
||||
$rows = buildGridRows($pdo, $pageIds, $config);
|
||||
|
||||
$out = [
|
||||
'success' => true,
|
||||
'total' => $total,
|
||||
'page' => $page,
|
||||
'per_page' => $perPage,
|
||||
'total_pages' => $totalPages,
|
||||
'rows' => $rows,
|
||||
];
|
||||
|
||||
// Tutti gli id del match (per "seleziona tutti"), solo su richiesta
|
||||
if ($wantAllIds) {
|
||||
$allStmt = $pdo->prepare("
|
||||
SELECT d.iddatadb FROM datadb d {$joinSql}
|
||||
WHERE {$whereSql}
|
||||
ORDER BY d.excelrow ASC, d.iddatadb ASC
|
||||
LIMIT " . FILTER_ALLIDS_CAP . "
|
||||
");
|
||||
$allStmt->execute($params);
|
||||
$out['all_ids'] = array_map('intval', $allStmt->fetchAll(PDO::FETCH_COLUMN));
|
||||
$out['all_ids_capped'] = $total > FILTER_ALLIDS_CAP;
|
||||
}
|
||||
|
||||
echo json_encode($out);
|
||||
} catch (Exception $e) {
|
||||
error_log('filter_records error: ' . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Endpoint: GET api/odata/AnagraficaCertestObject
|
||||
$endpoint = 'AnagraficaCertestObject';
|
||||
|
||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||
$options = []; // es: ['$top' => 100]
|
||||
|
||||
// Debug: salva URL usata
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$query = http_build_query($options);
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/anagrafica_certest_object_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Endpoint: GET api/odata/AnagraficaCertestService
|
||||
$endpoint = 'AnagraficaCertestService';
|
||||
|
||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||
$options = []; // es: ['$top' => 100]
|
||||
|
||||
// Debug: salva URL usata
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$query = http_build_query($options);
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/anagrafica_certest_service_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Endpoint per recuperare le Analisi
|
||||
$endpoint = 'Analisi';
|
||||
|
||||
// Opzioni OData
|
||||
$options = [
|
||||
// Restituisce solo i campi principali utili
|
||||
'$select' => 'IdAnalisi,Codice,NomeAnalisi,ClientiAbilitati,MatriciAbilitate,IsGenerico,Tipo,ParentKey,SelezionabileSuWeb',
|
||||
|
||||
// Solo analisi effettivamente selezionabili sul web
|
||||
'$filter' => 'SelezionabileSuWeb eq true',
|
||||
|
||||
// Ordinamento alfabetico per nome analisi
|
||||
'$orderby' => 'NomeAnalisi asc'
|
||||
];
|
||||
|
||||
// Debug: salva URL usato
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$query = http_build_query($options);
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_analisi_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/analisi_response.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
||||
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()], JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -1,68 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
|
||||
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||
$debug = isset($_GET['debug']) ? (int)$_GET['debug'] : 0;
|
||||
|
||||
if ($idCliente <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid id_cliente']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
if ($idMatrice > 0) {
|
||||
$expandExpr = "AnalisiAbilitate(\$filter=Matrice/IdMatrice eq $idMatrice)";
|
||||
} else {
|
||||
$expandExpr = "AnalisiAbilitate";
|
||||
}
|
||||
|
||||
// Encode only the $expand expression because it contains spaces, slash and parentheses
|
||||
$expandEncoded = rawurlencode($expandExpr);
|
||||
$endpoint = "Cliente($idCliente)?\$expand={$expandEncoded}";
|
||||
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$full_url = $base_url . $endpoint;
|
||||
|
||||
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
$data = $api->get($endpoint, []);
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_by_cliente_matrice_response.json',
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
if ($debug === 1) {
|
||||
echo json_encode([
|
||||
'endpoint' => $endpoint,
|
||||
'full_url' => $full_url,
|
||||
'raw_response' => $data
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$analisi = $data['AnalisiAbilitate'] ?? [];
|
||||
|
||||
echo json_encode([
|
||||
'value' => $analisi,
|
||||
'count' => is_array($analisi) ? count($analisi) : 0
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log_analisi.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,56 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||
|
||||
if ($idMatrice <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid id_matrice']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
/**
|
||||
* OData hypothesis:
|
||||
* - Expand enabled matrices
|
||||
* - Return only selectable analyses
|
||||
* - Include generic analyses OR analyses enabled for the given matrix
|
||||
*
|
||||
* This endpoint must be verified against the real VisualLims API metadata.
|
||||
*/
|
||||
$filter = rawurlencode("SelezionabileSuWeb eq true and (IsGenerico eq true or MatriciAbilitate/any(m:m/IdMatrice eq $idMatrice))");
|
||||
$expand = rawurlencode("MatriciAbilitate");
|
||||
$endpoint = "Analisi?\$top=10";
|
||||
|
||||
// Debug URL
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$full_url = $base_url . $endpoint;
|
||||
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
$data = $api->get($endpoint, []);
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_by_matrice_response.json',
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
$analisi = $data['value'] ?? [];
|
||||
|
||||
echo json_encode(['value' => $analisi], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log_analisi.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,85 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$idMatrice = isset($_GET['id_matrice']) ? (int)$_GET['id_matrice'] : 0;
|
||||
$debug = isset($_GET['debug']) ? (int)$_GET['debug'] : 0;
|
||||
|
||||
if ($idMatrice <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid id_matrice']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||
|
||||
$webOnly = isset($_GET['web_only']) ? (int)$_GET['web_only'] : 1;
|
||||
$refresh = isset($_GET['refresh']) ? (int)$_GET['refresh'] : 0;
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
|
||||
$filterString = "Matrice/IdMatrice eq $idMatrice";
|
||||
|
||||
if ($webOnly === 1) {
|
||||
$filterString .= " and SelezionabileSuWeb eq true";
|
||||
}
|
||||
|
||||
$filter = rawurlencode($filterString);
|
||||
$endpoint = "Analisi?\$filter={$filter}";
|
||||
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$full_url = $base_url . $endpoint;
|
||||
|
||||
// Cache file matches the one warm_cache.php writes (web_only=1 is what the analysis modal asks for).
|
||||
// Caching skipped in simulate mode to avoid polluting real-data cache files.
|
||||
$cache_file = __DIR__ . '/cache/analisi_matrice_' . $idMatrice . ($webOnly === 1 ? '' : '_all') . '.json';
|
||||
$data = null;
|
||||
$fromCache = false;
|
||||
|
||||
if (!$simulate && !$refresh && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||
$data = json_decode(file_get_contents($cache_file), true);
|
||||
$fromCache = is_array($data);
|
||||
}
|
||||
|
||||
if (!$fromCache) {
|
||||
$data = $api->get($endpoint, []);
|
||||
if (!$simulate) {
|
||||
file_put_contents($cache_file, json_encode(['value' => $data['value'] ?? []], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug === 1) {
|
||||
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_by_matrice_response.json',
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
echo json_encode([
|
||||
'endpoint' => $endpoint,
|
||||
'full_url' => $full_url,
|
||||
'from_cache' => $fromCache,
|
||||
'raw_response' => $data
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$analisi = $data['value'] ?? [];
|
||||
|
||||
echo json_encode([
|
||||
'value' => $analisi,
|
||||
'count' => is_array($analisi) ? count($analisi) : 0
|
||||
], JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log_analisi.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,95 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Campione hardcoded del tuo esempio
|
||||
$idCampione = 749027;
|
||||
|
||||
// Analisi che stai provando ad assegnare
|
||||
$targetRecordKey = '11;1218320';
|
||||
$targetIdAnalisi = '1218320';
|
||||
|
||||
// Endpoint: recupera il campione espandendo le analisi abilitate
|
||||
$endpoint = "Campione($idCampione)";
|
||||
|
||||
// ATTENZIONE:
|
||||
// nella tua classe normalmente le opzioni vengono trasformate in query string.
|
||||
// Qui serve: ?$expand=AnalisiAbilitate
|
||||
$options = [
|
||||
'$expand' => 'AnalisiAbilitate'
|
||||
];
|
||||
|
||||
// Debug: salva URL usato
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$query = http_build_query($options);
|
||||
$query = urldecode($query); // rende leggibile $expand invece di %24expand
|
||||
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_url_analisi_abilitate.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Recupera AnalisiAbilitate dalla risposta
|
||||
$analisiAbilitate = $data['AnalisiAbilitate'] ?? [];
|
||||
|
||||
// Alcune API OData possono restituire collection dentro "value"
|
||||
if (isset($analisiAbilitate['value']) && is_array($analisiAbilitate['value'])) {
|
||||
$analisiAbilitate = $analisiAbilitate['value'];
|
||||
}
|
||||
|
||||
// Cerca se il RecordKey / IdAnalisi che stai usando è effettivamente assegnabile
|
||||
$matches = [];
|
||||
|
||||
foreach ($analisiAbilitate as $analisi) {
|
||||
$recordKey = isset($analisi['RecordKey']) ? (string)$analisi['RecordKey'] : '';
|
||||
$idAnalisi = isset($analisi['IdAnalisi']) ? (string)$analisi['IdAnalisi'] : '';
|
||||
|
||||
if ($recordKey === $targetRecordKey || $idAnalisi === $targetIdAnalisi) {
|
||||
$matches[] = $analisi;
|
||||
}
|
||||
}
|
||||
|
||||
// Output diagnostico
|
||||
$output = [
|
||||
'success' => true,
|
||||
'idCampione' => $idCampione,
|
||||
'request_url' => $full_url,
|
||||
'targetRecordKey' => $targetRecordKey,
|
||||
'targetIdAnalisi' => $targetIdAnalisi,
|
||||
'enabled_analyses_count' => is_array($analisiAbilitate) ? count($analisiAbilitate) : 0,
|
||||
'target_found' => count($matches) > 0,
|
||||
'target_matches' => $matches,
|
||||
'analisi_abilitate' => $analisiAbilitate,
|
||||
'raw_response' => $data
|
||||
];
|
||||
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_abilitate_campione_749027_response.json',
|
||||
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log_analisi_abilitate.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -1,88 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Campione hardcoded del tuo esempio
|
||||
$idCampione = 749027;
|
||||
|
||||
// Matrice attesa dal log STEP 6
|
||||
$expectedMatriceId = 6430;
|
||||
|
||||
// Endpoint con expand della Matrice
|
||||
$endpoint = "Campione($idCampione)";
|
||||
$options = [
|
||||
'$expand' => 'Matrice'
|
||||
];
|
||||
|
||||
// Debug URL
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$query = http_build_query($options);
|
||||
$queryReadable = urldecode($query);
|
||||
|
||||
$full_url = $base_url . $endpoint . ($queryReadable ? '?' . $queryReadable : '');
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/last_url_check_matrice.txt',
|
||||
$full_url . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
// Recupero Matrice dalla response
|
||||
$matrice = $data['Matrice'] ?? null;
|
||||
|
||||
$actualMatriceId = null;
|
||||
|
||||
if (is_array($matrice)) {
|
||||
// Provo i nomi più probabili
|
||||
$actualMatriceId = $matrice['IdMatrice']
|
||||
?? $matrice['idMatrice']
|
||||
?? $matrice['Id']
|
||||
?? $matrice['ID']
|
||||
?? null;
|
||||
}
|
||||
|
||||
$matrice_ok = ((int)$actualMatriceId === (int)$expectedMatriceId);
|
||||
|
||||
$output = [
|
||||
'success' => true,
|
||||
'idCampione' => $idCampione,
|
||||
'expectedMatriceId' => $expectedMatriceId,
|
||||
'actualMatriceId' => $actualMatriceId,
|
||||
'matrice_ok' => $matrice_ok,
|
||||
'request_url' => $full_url,
|
||||
'matrice' => $matrice,
|
||||
'raw_response' => $data
|
||||
];
|
||||
|
||||
// Salva JSON completo
|
||||
file_put_contents(
|
||||
__DIR__ . '/check_matrice_campione_749027_response.json',
|
||||
json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
echo json_encode($output, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log_check_matrice.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
|
||||
if ($idCliente <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid id_cliente']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Build endpoint with OData $expand WITHOUT encoding '$' as %24
|
||||
$endpoint = "Cliente($idCliente)?\$expand=Responsabili";
|
||||
|
||||
// Debug URL (real final URL)
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$full_url = $base_url . $endpoint;
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Call API: options must be empty because expand is already in endpoint
|
||||
$data = $api->get($endpoint, []);
|
||||
|
||||
|
||||
file_put_contents(__DIR__ . '/cliente_responsabili_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
// Return only the list (standard shape used by the frontend)
|
||||
$responsabili = $data['Responsabili'] ?? [];
|
||||
echo json_encode(['value' => $responsabili]);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,102 +1,24 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Torna al livello di public per trovare vendor/
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
// Disabilita la visualizzazione degli errori PHP per evitare output HTML
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$data = $api->get("Cliente"); // Recupera i clienti
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
echo json_encode(['value' => $fakeClients]);
|
||||
exit;
|
||||
}
|
||||
// Salva la risposta in un file per debug
|
||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data));
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
// Costruisce query string con encoding corretto
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Componi endpoint finale
|
||||
$endpoint = "Cliente?$queryString";
|
||||
|
||||
// Funzione per eseguire la chiamata con retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
// Tenta la chiamata API
|
||||
$data = $api->get($endpoint);
|
||||
// Salva risposta per debug
|
||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
|
||||
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
|
||||
// Forza il refresh del token
|
||||
try {
|
||||
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
|
||||
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
// Ritarda leggermente prima del retry
|
||||
usleep(500000); // 500ms
|
||||
continue;
|
||||
}
|
||||
// Altri errori non gestiti dal retry
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Esegui la chiamata con retry
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
$json = json_encode($data);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
echo json_encode([
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -1,127 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
echo json_encode(['value' => $fakeClients]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── DEBUG MODE ───────────────────────────────────────────────────────
|
||||
// Chiamata: get_clienti.php?debug=1
|
||||
// Bypassa cache e $select per vedere TUTTI i campi grezzi restituiti
|
||||
// dall'API per l'entità Cliente. Usare solo per ispezione manuale,
|
||||
// NON lasciare linkato/usato in produzione dal frontend.
|
||||
if (($_GET['debug'] ?? '') === '1') {
|
||||
$debugParams = [
|
||||
'$top' => 5,
|
||||
'$orderby' => 'Nominativo asc'
|
||||
// Nessun $select: chiediamo tutti i campi disponibili
|
||||
];
|
||||
$debugEndpoint = "Cliente?" . http_build_query($debugParams);
|
||||
$debugData = $api->get($debugEndpoint);
|
||||
$debugClients = $debugData['value'] ?? $debugData;
|
||||
|
||||
echo json_encode([
|
||||
'mode' => 'debug - no $select, cache bypassed',
|
||||
'top_level_keys' => is_array($debugData) ? array_keys($debugData) : null,
|
||||
'fields_found_in_first_record' => isset($debugClients[0]) ? array_keys($debugClients[0]) : null,
|
||||
'sample_records' => $debugClients,
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
// ── FINE DEBUG MODE ──────────────────────────────────────────────────
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
// Costruisce query string con encoding corretto
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Componi endpoint finale
|
||||
$endpoint = "Cliente?$queryString";
|
||||
|
||||
// Funzione per eseguire la chiamata con retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
// Tenta la chiamata API
|
||||
$data = $api->get($endpoint);
|
||||
// Salva risposta per debug
|
||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
|
||||
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
|
||||
// Forza il refresh del token
|
||||
try {
|
||||
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
|
||||
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
// Ritarda leggermente prima del retry
|
||||
usleep(500000); // 500ms
|
||||
continue;
|
||||
}
|
||||
// Altri errori non gestiti dal retry
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Esegui la chiamata con retry
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
$json = json_encode($data);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
@@ -1,103 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
$stmt = $pdo->query("
|
||||
SELECT DISTINCT idclient
|
||||
FROM datadb
|
||||
WHERE idclient IS NOT NULL
|
||||
AND idclient > 0
|
||||
ORDER BY idclient ASC
|
||||
");
|
||||
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
|
||||
echo json_encode(['value' => $fakeClients], JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Endpoint senza filtri: recupera tutto
|
||||
$endpoint = 'Cliente?$top=50';
|
||||
|
||||
// Funzione per eseguire la chiamata con retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
// Save response for debug
|
||||
file_put_contents(
|
||||
__DIR__ . '/clienti_response.json',
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
|
||||
// Retry only for specific auth/token related issue
|
||||
if (
|
||||
strpos($errorMessage, 'HTTP 400') !== false &&
|
||||
strpos($errorMessage, 'Cannot persist the object') !== false
|
||||
) {
|
||||
try {
|
||||
if (method_exists($api, 'refreshToken')) {
|
||||
$api->refreshToken();
|
||||
error_log("Tentativo {$retry}: refresh token eseguito per endpoint {$endpoint}");
|
||||
} else {
|
||||
throw new Exception('Il metodo refreshToken() non esiste in VisualLimsApiClient');
|
||||
}
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
|
||||
usleep(500000); // 500ms
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per {$endpoint}");
|
||||
}
|
||||
|
||||
// Esegui la chiamata
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
echo json_encode($data, JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
|
||||
error_log("Errore in get_clienti.php: " . json_encode($errorResponse, JSON_UNESCAPED_UNICODE));
|
||||
echo json_encode($errorResponse, JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
echo json_encode(['value' => $fakeClients]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
// Costruisce query string con encoding corretto
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Componi endpoint finale
|
||||
$endpoint = "Cliente?$queryString";
|
||||
|
||||
// Funzione per eseguire la chiamata con retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
// Tenta la chiamata API
|
||||
$data = $api->get($endpoint);
|
||||
// Salva risposta per debug
|
||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
|
||||
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
|
||||
// Forza il refresh del token
|
||||
try {
|
||||
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
|
||||
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
// Ritarda leggermente prima del retry
|
||||
usleep(500000); // 500ms
|
||||
continue;
|
||||
}
|
||||
// Altri errori non gestiti dal retry
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Esegui la chiamata con retry
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
$json = json_encode($data);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
@@ -21,26 +21,17 @@ try {
|
||||
}
|
||||
|
||||
$results = [];
|
||||
$cacheDir = __DIR__ . '/cache';
|
||||
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||
|
||||
foreach ($fieldIds as $customFieldId) {
|
||||
$cacheFile = $cacheDir . '/customfield_' . $customFieldId . '.json';
|
||||
|
||||
// Use cache if fresh (1 hour)
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
$results[$customFieldId] = json_decode(file_get_contents($cacheFile), true);
|
||||
continue;
|
||||
}
|
||||
|
||||
$endpoint = "CustomField($customFieldId)?\$expand=CustomFieldsValues";
|
||||
$data = $api->get($endpoint);
|
||||
$values = $data['CustomFieldsValues'] ?? [];
|
||||
$results[$customFieldId] = $values;
|
||||
|
||||
file_put_contents($cacheFile, json_encode($values));
|
||||
$results[$customFieldId] = $data['CustomFieldsValues'] ?? [];
|
||||
}
|
||||
|
||||
// Debug ფაილი
|
||||
file_put_contents(__DIR__ . '/customfield_values_response.json', json_encode($results));
|
||||
|
||||
echo json_encode($results);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
<?php
|
||||
// get_fixed_field_data.php
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$field = $_GET['field'] ?? ''; // es: MoltiplicatorePrezzo, AnagraficaCertestObject, ...
|
||||
|
||||
if (!$field) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Parametro "field" mancante']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
$data = null;
|
||||
$endpoint = null;
|
||||
$cache_file = null;
|
||||
$options = []; // qui puoi aggiungere filtri/ordering per campo se serve
|
||||
|
||||
try {
|
||||
switch ($field) {
|
||||
case 'MoltiplicatorePrezzo':
|
||||
$endpoint = 'MoltiplicatorePrezzi';
|
||||
$cache_file = __DIR__ . '/cache/moltiplicatori_prezzo.json';
|
||||
break;
|
||||
|
||||
case 'AnagraficaCertestObject':
|
||||
$endpoint = 'AnagraficaCertestObject';
|
||||
$cache_file = __DIR__ . '/cache/anagrafica_certest_object.json';
|
||||
break;
|
||||
|
||||
case 'AnagraficaCertestService':
|
||||
$endpoint = 'AnagraficaCertestService';
|
||||
$cache_file = __DIR__ . '/cache/anagrafica_certest_service.json';
|
||||
break;
|
||||
|
||||
case 'ClienteResponsabile':
|
||||
$id_cliente = (int)($_GET['id_cliente'] ?? 0);
|
||||
if ($id_cliente <= 0) {
|
||||
if (!$simulate) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Manca o invalido id_cliente']);
|
||||
exit;
|
||||
}
|
||||
$id_cliente = 1; // dummy — mock ignores the actual ID
|
||||
}
|
||||
$endpoint = "Cliente($id_cliente)?\$expand=Responsabili";
|
||||
$cache_file = __DIR__ . '/cache/cliente_responsabili_' . $id_cliente . '.json';
|
||||
break;
|
||||
|
||||
// case 'FuturoCampo5':
|
||||
// $endpoint = 'QualcosaElse';
|
||||
// break;
|
||||
|
||||
default:
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => "Campo non supportato: $field"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Caching skipped in simulate mode to avoid polluting real-data cache files
|
||||
if (!$simulate && $cache_file && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||
$data = json_decode(file_get_contents($cache_file), true);
|
||||
} else {
|
||||
$data = $api->get($endpoint, $options);
|
||||
|
||||
if (!$simulate && $cache_file) {
|
||||
file_put_contents($cache_file, json_encode($data, JSON_PRETTY_PRINT));
|
||||
}
|
||||
}
|
||||
|
||||
// Log ultimo URL (opzionale, per debug)
|
||||
$full_url = $base_url . $endpoint . ($options ? '?' . http_build_query($options) : '');
|
||||
file_put_contents(__DIR__ . '/last_urls.log', date('c') . " - $field - $full_url\n", FILE_APPEND);
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . " [$field] " . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; }
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
// Read from matrici cache (populated by get_matrici.php / warm_cache.php)
|
||||
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||
if (file_exists($cacheFile)) {
|
||||
$data = json_decode(file_get_contents($cacheFile), true);
|
||||
$matrici = $data['value'] ?? [];
|
||||
} else {
|
||||
// Fallback: trigger cache creation
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$apiData = $api->get('Matrice');
|
||||
$matrici = [];
|
||||
foreach (($apiData['value'] ?? []) as $item) {
|
||||
$nome = $item['NomeMatrice'] ?? '';
|
||||
if ($nome !== '' && substr($nome, 0, 1) !== '*') {
|
||||
$matrici[] = ['IdMatrice' => $item['IdMatrice'], 'NomeMatrice' => $nome, 'MacroMatrice' => $item['MacroMatrice'] ?? null];
|
||||
}
|
||||
}
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, json_encode(['success' => true, 'value' => $matrici]));
|
||||
}
|
||||
|
||||
// Extract unique MacroMatrice values
|
||||
$macroValues = [];
|
||||
foreach ($matrici as $m) {
|
||||
$macro = $m['MacroMatrice'] ?? null;
|
||||
if ($macro !== null && $macro !== '' && substr($macro, 0, 1) !== '*') {
|
||||
$macroValues[$macro] = true;
|
||||
}
|
||||
}
|
||||
$macroMatrici = array_keys($macroValues);
|
||||
sort($macroMatrici);
|
||||
|
||||
echo json_encode(['success' => true, 'value' => $macroMatrici]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,47 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
if (!Auth::check()) { http_response_code(401); echo json_encode(['error' => 'Unauthorized']); exit; }
|
||||
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/matrici.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$data = $api->get('Matrice');
|
||||
|
||||
$matrici = [];
|
||||
if (isset($data['value']) && is_array($data['value'])) {
|
||||
foreach ($data['value'] as $item) {
|
||||
$nome = $item['NomeMatrice'] ?? '';
|
||||
if ($nome !== '' && substr($nome, 0, 1) !== '*') {
|
||||
$matrici[] = [
|
||||
'IdMatrice' => $item['IdMatrice'],
|
||||
'NomeMatrice' => $nome,
|
||||
'MacroMatrice' => $item['MacroMatrice'] ?? null,
|
||||
];
|
||||
}
|
||||
}
|
||||
usort($matrici, fn($a, $b) => strcasecmp($a['NomeMatrice'], $b['NomeMatrice']));
|
||||
}
|
||||
|
||||
$json = json_encode(['success' => true, 'value' => $matrici]);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php'; // Risale a root/vendor/
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
// Set JSON header
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Debug: Log the path where we expect the .env file
|
||||
$envPath = dirname(__DIR__, 2);
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Carica il file .env dalla root del progetto
|
||||
try {
|
||||
$dotenv = Dotenv::createImmutable($envPath);
|
||||
$dotenv->load();
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
echo json_encode(['success' => false, 'message' => 'Errore caricamento configurazione: ' . $e->getMessage()]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Recupera le variabili d'ambiente
|
||||
$dbHost = $_ENV['DB_HOST'];
|
||||
$dbName = $_ENV['DB_DATABASE'];
|
||||
$dbUser = $_ENV['DB_USERNAME'];
|
||||
$dbPass = $_ENV['DB_PASSWORD'];
|
||||
$dbPrefix = $_ENV['DB_PREFIX'];
|
||||
|
||||
// Debug: Log database connection details (excluding password)
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Connessione al database MySQL
|
||||
try {
|
||||
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
|
||||
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||
} catch (PDOException $e) {
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
echo json_encode(['success' => false, 'message' => 'Errore connessione al database: ' . $e->getMessage()]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
try {
|
||||
// Query per recuperare le matrici, includendo MacroMatrice, escludendo quelle che iniziano con '*' e ordinandole
|
||||
$query = "SELECT IdMatrice, NomeMatrice, MacroMatrice FROM {$dbPrefix}matrici WHERE NomeMatrice NOT LIKE '*%' ORDER BY NomeMatrice ASC";
|
||||
$stmt = $pdo->prepare($query);
|
||||
$stmt->execute();
|
||||
$matrici = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Debug: Log del numero di matrici recuperate
|
||||
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Retrieved ' . count($matrici) . ' matrices from database' . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Restituisci risposta JSON
|
||||
echo json_encode(['success' => true, 'value' => $matrici]);
|
||||
} catch (PDOException $e) {
|
||||
// Log errore e restituisci risposta di errore
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore nel recupero delle matrici: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel recupero delle matrici: ' . $e->getMessage()]);
|
||||
exit(1);
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
// Endpoint per recuperare i Moltiplicatori Prezzo
|
||||
// (dal documento: GET api/odata/MoltiplicatorePrezzi)
|
||||
$endpoint = 'MoltiplicatorePrezzi';
|
||||
|
||||
// Opzionale: parametri OData ($top, $filter, $orderby, ecc.)
|
||||
$options = [
|
||||
'$orderby' => 'Descrizione asc'
|
||||
];
|
||||
|
||||
// Debug: salva URL usata
|
||||
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
$queryParts = [];
|
||||
foreach ($options as $k => $v) {
|
||||
// mantieni il $ nella chiave, encoda solo il valore
|
||||
$queryParts[] = $k . '=' . rawurlencode($v);
|
||||
}
|
||||
$query = implode('&', $queryParts);
|
||||
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
|
||||
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
|
||||
// Chiamata API
|
||||
$data = $api->get($endpoint, $options);
|
||||
// ✅ Force sort locally by "Descrizione" (A→Z)
|
||||
if (isset($data['value']) && is_array($data['value'])) {
|
||||
usort($data['value'], function ($a, $b) {
|
||||
$da = isset($a['Descrizione']) ? trim((string)$a['Descrizione']) : '';
|
||||
$db = isset($b['Descrizione']) ? trim((string)$b['Descrizione']) : '';
|
||||
return strcasecmp($da, $db); // case-insensitive alphabetical
|
||||
});
|
||||
}
|
||||
// Salva il JSON in locale
|
||||
file_put_contents(__DIR__ . '/moltiplicatori_prezzo_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,38 +0,0 @@
|
||||
<?php
|
||||
require_once __DIR__ . '/include/headscript.php'; // o il tuo bootstrap standard
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
try {
|
||||
if (!isset($_GET['iddatadb']) || !is_numeric($_GET['iddatadb'])) {
|
||||
echo json_encode(['success' => true, 'field' => null]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$iddatadb = (int)$_GET['iddatadb'];
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// 1) prendo templateid da datadb
|
||||
// 2) cerco max 1 record in template_mapping con is_visible_parts=1
|
||||
$sql = "
|
||||
SELECT tm.field_id, tm.field_label, tm.data_type, tm.has_list
|
||||
FROM datadb d
|
||||
JOIN template_mapping tm ON tm.template_id = d.templateid
|
||||
WHERE d.iddatadb = ?
|
||||
AND tm.is_visible_parts = 1
|
||||
ORDER BY tm.id ASC
|
||||
LIMIT 1
|
||||
";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute([$iddatadb]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'field' => $row ? $row : null
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,256 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
/**
|
||||
* Uso:
|
||||
* rapporto_full_by_codice.php?codice=2621521
|
||||
* rapporto_full_by_codice.php?codice=2621521&download=1
|
||||
*/
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||
$downloadPdf = isset($_GET['download']) && $_GET['download'] == '1';
|
||||
|
||||
if ($codiceRapporto === '') {
|
||||
throw new Exception("Parametro codice mancante. Usa ?codice=2621521");
|
||||
}
|
||||
|
||||
$debugDir = __DIR__ . '/logs/lims_rapporti/';
|
||||
$pdfDir = __DIR__ . '/pdf/rapporti/';
|
||||
|
||||
if (!is_dir($debugDir)) mkdir($debugDir, 0755, true);
|
||||
if (!is_dir($pdfDir)) mkdir($pdfDir, 0755, true);
|
||||
|
||||
$codiceRapportoSafe = str_replace("'", "''", $codiceRapporto);
|
||||
|
||||
/**
|
||||
* STEP 1 - Search IdRapporto
|
||||
*/
|
||||
$searchParams = [
|
||||
'$filter' => "CodiceRapporto eq '{$codiceRapportoSafe}'",
|
||||
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||
'$top' => 1
|
||||
];
|
||||
|
||||
$searchEndpoint = "Rapporto?" . http_build_query($searchParams);
|
||||
$searchData = $api->get($searchEndpoint);
|
||||
|
||||
$rapporti = $searchData['value'] ?? [];
|
||||
|
||||
if (empty($rapporti)) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Nessun rapporto trovato per questo CodiceRapporto.',
|
||||
'codice_rapporto' => $codiceRapporto
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rapportoBase = $rapporti[0];
|
||||
$idRapporto = intval($rapportoBase['IdRapporto'] ?? 0);
|
||||
|
||||
if (!$idRapporto) {
|
||||
throw new Exception("IdRapporto non trovato nella risposta.");
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 2 - Dettaglio Rapporto
|
||||
*/
|
||||
$clienteExpandError = null;
|
||||
$detailEndpoint = "Rapporto({$idRapporto})?\$expand=Cliente,RapportiFiles,CampioniDatiRapporto";
|
||||
|
||||
try {
|
||||
$detailData = $api->get($detailEndpoint);
|
||||
} catch (Exception $e) {
|
||||
$clienteExpandError = $e->getMessage();
|
||||
$detailEndpoint = "Rapporto({$idRapporto})?\$expand=RapportiFiles,CampioniDatiRapporto";
|
||||
$detailData = $api->get($detailEndpoint);
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
$debugDir . "rapporto_{$codiceRapportoSafe}_light.json",
|
||||
json_encode($detailData, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
$cliente = $detailData['Cliente'] ?? null;
|
||||
$rapportiFiles = $detailData['RapportiFiles'] ?? [];
|
||||
$campioniDatiRapporto = $detailData['CampioniDatiRapporto'] ?? [];
|
||||
|
||||
// ====================== CLIENTE ======================
|
||||
$clienteInfo = [
|
||||
'found' => is_array($cliente),
|
||||
'id_cliente' => $cliente['IdCliente'] ?? $cliente['Id'] ?? null,
|
||||
'codice_cliente' => $cliente['CodiceCliente'] ?? $cliente['Codice'] ?? null,
|
||||
'nome_cliente' => $cliente['Nominativo'] ?? $cliente['RagioneSociale'] ?? $cliente['Nome'] ?? $cliente['Descrizione'] ?? null,
|
||||
'partita_iva' => $cliente['PartitaIva'] ?? null,
|
||||
'codice_fiscale' => $cliente['CodiceFiscale'] ?? null
|
||||
];
|
||||
|
||||
// ====================== FILE PDF ======================
|
||||
$selectedRapportoFile = null;
|
||||
$idRapportoFile = null;
|
||||
$pdfFileName = $codiceRapporto . '.pdf';
|
||||
$pdfFullPath = $pdfDir . $pdfFileName;
|
||||
|
||||
if (is_array($rapportiFiles) && count($rapportiFiles) > 0) {
|
||||
foreach ($rapportiFiles as $file) {
|
||||
$fileName = $file['FileName'] ?? '';
|
||||
$tipoRapporto = $file['TipoRapporto'] ?? '';
|
||||
$categoria = $file['Categoria'] ?? '';
|
||||
|
||||
if (
|
||||
stripos($fileName, '.pdf') !== false ||
|
||||
stripos($tipoRapporto, 'Rapporto') !== false ||
|
||||
stripos($categoria, 'Rapporti') !== false
|
||||
) {
|
||||
$selectedRapportoFile = $file;
|
||||
$idRapportoFile = $file['IdRapportoFile'] ?? null;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$selectedRapportoFile && count($rapportiFiles) > 0) {
|
||||
$selectedRapportoFile = $rapportiFiles[0];
|
||||
$idRapportoFile = $rapportiFiles[0]['IdRapportoFile'] ?? null;
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== DOWNLOAD PDF - DEBUG AVANZATO ======================
|
||||
$pdfDownloaded = false;
|
||||
$pdfError = null;
|
||||
$endpointUsato = null;
|
||||
|
||||
if ($downloadPdf && $idRapportoFile) {
|
||||
|
||||
$tentativi = [
|
||||
"RapportoFile({$idRapportoFile})/\$value",
|
||||
"RapportoFile({$idRapportoFile})/Contenuto/\$value",
|
||||
"RapportoFile({$idRapportoFile})/File/\$value",
|
||||
"Rapporto({$idRapporto})/RapportiFiles({$idRapportoFile})/\$value",
|
||||
"Rapporto({$idRapporto})/RapportoFile/\$value",
|
||||
"GetRapportoFile?IdRapportoFile={$idRapportoFile}",
|
||||
"RapportoFile/Download?Id={$idRapportoFile}",
|
||||
"Rapporto/DownloadFile?IdRapporto={$idRapporto}"
|
||||
];
|
||||
|
||||
foreach ($tentativi as $ep) {
|
||||
try {
|
||||
$pdfContent = $api->getRaw($ep);
|
||||
|
||||
if (strlen($pdfContent) > 2000) { // PDF decente deve essere più grande
|
||||
file_put_contents($pdfFullPath, $pdfContent);
|
||||
$pdfDownloaded = true;
|
||||
$endpointUsato = $ep;
|
||||
$pdfError = "SUCCESSO con: " . $ep;
|
||||
break;
|
||||
} else {
|
||||
$pdfError = "Contenuto troppo piccolo con: " . $ep;
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
$pdfError = "Fallito con " . $ep . " → " . $e->getMessage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== CAMPIONI + RATING (ripristinato dal tuo originale) ======================
|
||||
$campioniSintesi = [];
|
||||
$ratingFinale = null;
|
||||
$ratingSource = null;
|
||||
$hasIrregolare = false;
|
||||
$hasPositiveResults = false;
|
||||
|
||||
if (is_array($campioniDatiRapporto)) {
|
||||
foreach ($campioniDatiRapporto as $campione) {
|
||||
$giudizioRapporto = $campione['GiudizioRapporto'] ?? null;
|
||||
$giudizioCertificato = $campione['GiudizioCertificato'] ?? null;
|
||||
$esitoGiudizioLMR = $campione['EsitoGiudizioLMR'] ?? null;
|
||||
$esitoGiudizioImpiego = $campione['EsitoGiudizioImpiego'] ?? null;
|
||||
$risultatiIrregolari = ($campione['RisultatiIrregolari'] ?? false) === true;
|
||||
$risultatiPositivi = ($campione['RisultatiPositivi'] ?? false) === true;
|
||||
|
||||
$campioniSintesi[] = [
|
||||
'id_campione_dati_rapporto' => $campione['IdCampioneDatiRapporto'] ?? null,
|
||||
'codice_campione' => $campione['CodiceCampione'] ?? null,
|
||||
'stato_campione' => $campione['StatoCampione'] ?? null,
|
||||
'stato_campione_web' => $campione['StatoCampioneWeb'] ?? null,
|
||||
'matrice' => $campione['Matrice'] ?? null,
|
||||
'macro_matrice' => $campione['MacroMatrice'] ?? null,
|
||||
'giudizio_rapporto' => $giudizioRapporto,
|
||||
'giudizio_certificato' => $giudizioCertificato,
|
||||
'esito_giudizio_lmr' => $esitoGiudizioLMR,
|
||||
'esito_giudizio_impiego' => $esitoGiudizioImpiego,
|
||||
'risultati_positivi' => $risultatiPositivi,
|
||||
'risultati_irregolari' => $risultatiIrregolari
|
||||
];
|
||||
|
||||
if (!$ratingFinale && !empty($giudizioRapporto)) {
|
||||
$ratingFinale = $giudizioRapporto;
|
||||
$ratingSource = 'CampioniDatiRapporto.GiudizioRapporto';
|
||||
}
|
||||
|
||||
if (!$ratingFinale && !empty($giudizioCertificato)) {
|
||||
$ratingFinale = $giudizioCertificato;
|
||||
$ratingSource = 'CampioniDatiRapporto.GiudizioCertificato';
|
||||
}
|
||||
|
||||
if ($risultatiIrregolari) $hasIrregolare = true;
|
||||
if ($risultatiPositivi) $hasPositiveResults = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback rating
|
||||
if (!$ratingFinale) {
|
||||
if ($hasIrregolare) {
|
||||
$ratingFinale = 'Irregolare';
|
||||
$ratingSource = 'RisultatiIrregolari';
|
||||
} elseif ($hasPositiveResults || count($campioniSintesi) > 0) {
|
||||
$ratingFinale = 'Regolare';
|
||||
$ratingSource = 'fallback';
|
||||
}
|
||||
}
|
||||
|
||||
// ====================== RISPOSTA FINALE ======================
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
|
||||
'codice_rapporto' => $codiceRapporto,
|
||||
'id_rapporto' => $idRapporto,
|
||||
|
||||
'search_endpoint' => $searchEndpoint,
|
||||
'detail_endpoint' => $detailEndpoint,
|
||||
'cliente_expand_error' => $clienteExpandError,
|
||||
|
||||
'rapporto_base' => $rapportoBase,
|
||||
'cliente' => $clienteInfo,
|
||||
|
||||
'rating_finale' => $ratingFinale,
|
||||
'rating_source' => $ratingSource,
|
||||
|
||||
'rapporti_files_count' => count($rapportiFiles),
|
||||
'selected_rapporto_file' => $selectedRapportoFile,
|
||||
|
||||
'pdf_file_name' => $pdfFileName,
|
||||
'pdf_path' => $pdfDownloaded ? $pdfFullPath : null,
|
||||
'pdf_downloaded' => $pdfDownloaded,
|
||||
'pdf_error' => $pdfError,
|
||||
|
||||
'campioni_count' => count($campioniSintesi),
|
||||
'campioni_sintesi' => $campioniSintesi,
|
||||
|
||||
'download_requested' => $downloadPdf
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -1,118 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
$commessa = trim($_GET['commessa'] ?? '');
|
||||
|
||||
if ($commessa === '') {
|
||||
throw new Exception("Parametro commessa mancante");
|
||||
}
|
||||
|
||||
$debugDir = __DIR__ . '/logs/lims_rapporto/';
|
||||
if (!is_dir($debugDir)) {
|
||||
mkdir($debugDir, 0755, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 1
|
||||
* Provo a cercare il rapporto con filtri leggeri.
|
||||
* NIENTE expand pesanti.
|
||||
*/
|
||||
$attempts = [];
|
||||
|
||||
$filters = [
|
||||
'CodiceCommessa' => "CodiceCommessa eq '{$commessa}'",
|
||||
'Commessa_CodiceCommessa' => "Commessa/CodiceCommessa eq '{$commessa}'",
|
||||
'Commessa_IdCommessa' => is_numeric($commessa) ? "Commessa/IdCommessa eq {$commessa}" : null,
|
||||
'Codice' => "Codice eq '{$commessa}'"
|
||||
];
|
||||
|
||||
foreach ($filters as $label => $filter) {
|
||||
if (!$filter) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
$options = [
|
||||
'$filter' => $filter,
|
||||
'$top' => 10
|
||||
];
|
||||
|
||||
$data = $api->get('Rapporto', $options);
|
||||
|
||||
$attempts[$label] = [
|
||||
'success' => true,
|
||||
'filter' => $filter,
|
||||
'records' => isset($data['value']) && is_array($data['value']) ? count($data['value']) : null,
|
||||
'data' => $data
|
||||
];
|
||||
|
||||
file_put_contents(
|
||||
$debugDir . "rapporto_{$commessa}_{$label}.json",
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
} catch (Exception $e) {
|
||||
$attempts[$label] = [
|
||||
'success' => false,
|
||||
'filter' => $filter,
|
||||
'error' => $e->getMessage()
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* STEP 2
|
||||
* Prendo solo eventuali rapporti trovati.
|
||||
*/
|
||||
$rapportiFound = [];
|
||||
|
||||
foreach ($attempts as $label => $attempt) {
|
||||
if (!$attempt['success']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$items = $attempt['data']['value'] ?? [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($items as $item) {
|
||||
$rapportiFound[] = [
|
||||
'matched_by' => $label,
|
||||
'rapporto' => $item
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'input_commessa' => $commessa,
|
||||
'message' => 'Ricerca leggera su Rapporto completata. Se trovi un rapporto, poi recuperiamo RapportiFiles solo per quello.',
|
||||
'rapporti_found_count' => count($rapportiFound),
|
||||
'rapporti_found' => $rapportiFound,
|
||||
'attempts_summary' => array_map(function ($a) {
|
||||
return [
|
||||
'success' => $a['success'],
|
||||
'filter' => $a['filter'],
|
||||
'records' => $a['records'] ?? null,
|
||||
'error' => $a['error'] ?? null
|
||||
];
|
||||
}, $attempts)
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -2,140 +2,25 @@
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json; charset=utf-8');
|
||||
header('Content-Type: application/json');
|
||||
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$rapporto_id = 515081;
|
||||
|
||||
// Esempi:
|
||||
// rapporto_by_codice_expand_step.php?codice=2541111&step=base
|
||||
// rapporto_by_codice_expand_step.php?codice=2541111&step=files
|
||||
// rapporto_by_codice_expand_step.php?codice=2541111&step=campioni
|
||||
// rapporto_by_codice_expand_step.php?codice=2541111&step=files_campioni
|
||||
// Costruzione manuale dell'endpoint con espansione annidata
|
||||
$endpoint = "Rapporto($rapporto_id)?\$expand=CampioniDatiRapporto(\$expand=AnalisiDatiRapporto,CustomFieldsDatiRapporto)";
|
||||
|
||||
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||
$step = trim($_GET['step'] ?? 'base');
|
||||
// Non passiamo options, già incluso nell'endpoint
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
if ($codiceRapporto === '') {
|
||||
throw new Exception("Parametro codice mancante. Usa ?codice=2541111");
|
||||
}
|
||||
|
||||
$allowedSteps = [
|
||||
'base' => '',
|
||||
'files' => 'RapportiFiles',
|
||||
'allegati' => 'RapportiAllegati',
|
||||
'campioni' => 'CampioniDatiRapporto',
|
||||
'files_campioni' => 'RapportiFiles,CampioniDatiRapporto'
|
||||
];
|
||||
|
||||
if (!array_key_exists($step, $allowedSteps)) {
|
||||
throw new Exception("Step non valido. Usa: " . implode(', ', array_keys($allowedSteps)));
|
||||
}
|
||||
|
||||
// Escape OData per eventuali apostrofi
|
||||
$codiceRapportoSafe = str_replace("'", "''", $codiceRapporto);
|
||||
|
||||
/*
|
||||
* STEP 1 - Trova IdRapporto partendo da CodiceRapporto.
|
||||
* Query leggera, con $select e $top=1.
|
||||
*/
|
||||
$searchParams = [
|
||||
'$filter' => "CodiceRapporto eq '{$codiceRapportoSafe}'",
|
||||
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||
'$top' => 1
|
||||
];
|
||||
|
||||
$searchEndpoint = "Rapporto?" . http_build_query($searchParams);
|
||||
|
||||
$searchData = $api->get($searchEndpoint);
|
||||
|
||||
$items = $searchData['value'] ?? [];
|
||||
|
||||
if (!is_array($items) || count($items) === 0) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => 'Nessun rapporto trovato per questo CodiceRapporto.',
|
||||
'codice_rapporto' => $codiceRapporto,
|
||||
'search_endpoint' => $searchEndpoint,
|
||||
'search_data' => $searchData
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
$rapportoBase = $items[0];
|
||||
$rapportoId = intval($rapportoBase['IdRapporto'] ?? 0);
|
||||
|
||||
if (!$rapportoId) {
|
||||
throw new Exception("IdRapporto non trovato nella risposta.");
|
||||
}
|
||||
|
||||
/*
|
||||
* STEP 2 - Se step=base, restituisco solo la ricerca base.
|
||||
*/
|
||||
if ($step === 'base') {
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'codice_rapporto' => $codiceRapporto,
|
||||
'id_rapporto' => $rapportoId,
|
||||
'step' => $step,
|
||||
'search_endpoint' => $searchEndpoint,
|
||||
'rapporto_base' => $rapportoBase
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* STEP 3 - Espande SOLO il rapporto trovato.
|
||||
*/
|
||||
$expandValue = $allowedSteps[$step];
|
||||
|
||||
$detailParams = [
|
||||
'$expand' => $expandValue
|
||||
];
|
||||
|
||||
$detailEndpoint = "Rapporto({$rapportoId})?" . http_build_query($detailParams);
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/last_rapporto_by_codice_expand_endpoint.txt',
|
||||
'[' . date('Y-m-d H:i:s') . '] SEARCH: ' . $searchEndpoint . PHP_EOL .
|
||||
'[' . date('Y-m-d H:i:s') . '] DETAIL: ' . $detailEndpoint . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
$detailData = $api->get($detailEndpoint);
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . "/rapporto_codice_{$codiceRapportoSafe}_{$step}.json",
|
||||
json_encode([
|
||||
'search' => $searchData,
|
||||
'detail' => $detailData
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'codice_rapporto' => $codiceRapporto,
|
||||
'id_rapporto' => $rapportoId,
|
||||
'step' => $step,
|
||||
'search_endpoint' => $searchEndpoint,
|
||||
'detail_endpoint' => $detailEndpoint,
|
||||
'rapporto_base' => $rapportoBase,
|
||||
'data' => $detailData
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
file_put_contents(__DIR__ . '/rapporto_expanded.json', json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
|
||||
@@ -1,94 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake users
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
$fakeUsers = [
|
||||
[
|
||||
'IdUtente' => 1001,
|
||||
'Nominativo' => 'Utente Simulato 1001'
|
||||
],
|
||||
[
|
||||
'IdUtente' => 1002,
|
||||
'Nominativo' => 'Utente Simulato 1002'
|
||||
]
|
||||
];
|
||||
|
||||
echo json_encode(['value' => $fakeUsers]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// OData parameters
|
||||
$params = [];
|
||||
|
||||
// Build query string
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Final endpoint
|
||||
$endpoint = "Utente?$queryString";
|
||||
|
||||
// Function to execute request with retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
// Save response for debug
|
||||
file_put_contents(__DIR__ . '/utenti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
|
||||
// Retry only on token/auth-related issue
|
||||
if (
|
||||
strpos($errorMessage, 'HTTP 400') !== false &&
|
||||
strpos($errorMessage, 'Cannot persist the object') !== false
|
||||
) {
|
||||
try {
|
||||
$api->refreshToken(); // must exist in VisualLimsApiClient
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
|
||||
usleep(500000); // 500 ms
|
||||
continue;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Execute request
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
echo json_encode($data);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
|
||||
error_log("Errore in get_utenti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
@@ -1,844 +0,0 @@
|
||||
/**
|
||||
* gridFilter.js — Selezione righe + filtri colonna a scomparsa per imported.php
|
||||
* Dipende da window.gridRenderer (gridRenderer.js). Includere DOPO gridRenderer.js.
|
||||
*/
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
let filtersActive = false;
|
||||
let restricted = false;
|
||||
const selected = new Set(); // iddatadb selezionati
|
||||
const colFilters = {}; // colKey -> testo filtro
|
||||
let restrictedIds = null; // Set di iddatadb visibili quando ristretto
|
||||
|
||||
function R() {
|
||||
return window.gridRenderer;
|
||||
}
|
||||
function data() {
|
||||
return R().getData();
|
||||
}
|
||||
function meta() {
|
||||
return R().getMeta();
|
||||
}
|
||||
|
||||
// ── Filtro SERVER-SIDE (paginato, come la lista normale ma filtrata) ──────
|
||||
// Il filtro gira su TUTTO il template; il server restituisce UNA PAGINA di righe
|
||||
// filtrate + il totale, e navighiamo le pagine del set filtrato via AJAX.
|
||||
const TEMPLATE_ID =
|
||||
new URLSearchParams(location.search).get("id") || (meta().templateId ?? "");
|
||||
const SHOW_ALL =
|
||||
new URLSearchParams(location.search).get("all_users") === "1";
|
||||
const IMPORTREF =
|
||||
new URLSearchParams(location.search).get("importref") || "";
|
||||
const FILTER_PER_PAGE =
|
||||
parseInt(new URLSearchParams(location.search).get("limit"), 10) || 20;
|
||||
|
||||
let serverFiltered = false;
|
||||
let filterPage = 1;
|
||||
let filterTotalPages = 1;
|
||||
let filterTotal = 0;
|
||||
let originalData = null; // snapshot della pagina originale (per ripristino)
|
||||
let filterReqSeq = 0;
|
||||
let showingSelection = false; // true quando mostriamo l'UNIONE dei selezionati
|
||||
let pagerFn = null; // paginazione corrente (applyServerFilter | showSelectedRecords)
|
||||
|
||||
function collectActiveFilters() {
|
||||
const cols = meta().columns || [];
|
||||
const out = [];
|
||||
for (const [key, term] of Object.entries(colFilters)) {
|
||||
if (!term || !String(term).trim()) continue;
|
||||
const col = cols.find((c) => String(c.key) === String(key));
|
||||
if (!col) continue;
|
||||
out.push({ key: String(key), type: col.type, term: String(term).trim() });
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
// Sostituisce il dataset del renderer in-place e ridisegna.
|
||||
function swapData(rows) {
|
||||
const arr = data();
|
||||
arr.length = 0;
|
||||
(rows || []).forEach((r) => arr.push(r));
|
||||
R().renderVisibleRows();
|
||||
}
|
||||
|
||||
function hidePagination(hide) {
|
||||
// Durante il filtro la paginazione server (per pagina, via URL) è fuorviante.
|
||||
document.querySelectorAll(".pager-bar").forEach((el) => {
|
||||
el.style.display = hide ? "none" : "";
|
||||
});
|
||||
}
|
||||
|
||||
// Pager del set filtrato (Prec / pag X di Y / Succ) nella toolbar.
|
||||
function updateFilterPager() {
|
||||
const el = document.getElementById("filterPager");
|
||||
if (!el) return;
|
||||
if (!serverFiltered) {
|
||||
el.style.display = "none";
|
||||
el.innerHTML = "";
|
||||
return;
|
||||
}
|
||||
el.style.display = "inline-flex";
|
||||
const label = showingSelection ? "selezionati" : "trovati";
|
||||
el.innerHTML =
|
||||
`<button type="button" class="btn btn-outline-secondary btn-sm" id="filterPrevBtn" ${filterPage <= 1 ? "disabled" : ""}>‹</button>` +
|
||||
`<span style="font-size:12px;color:#333;">${filterTotal} ${label} · pag ${filterPage}/${filterTotalPages}</span>` +
|
||||
`<button type="button" class="btn btn-outline-secondary btn-sm" id="filterNextBtn" ${filterPage >= filterTotalPages ? "disabled" : ""}>›</button>`;
|
||||
const go = pagerFn || applyServerFilter;
|
||||
const prev = document.getElementById("filterPrevBtn");
|
||||
const next = document.getElementById("filterNextBtn");
|
||||
if (prev) prev.addEventListener("click", () => go(filterPage - 1));
|
||||
if (next) next.addEventListener("click", () => go(filterPage + 1));
|
||||
}
|
||||
|
||||
function restoreOriginal() {
|
||||
serverFiltered = false;
|
||||
filterPage = 1;
|
||||
filterTotalPages = 1;
|
||||
filterTotal = 0;
|
||||
if (originalData) swapData(originalData);
|
||||
updateFilterPager();
|
||||
hidePagination(false);
|
||||
updateToolbar();
|
||||
}
|
||||
|
||||
async function applyServerFilter(page = 1) {
|
||||
const active = collectActiveFilters();
|
||||
if (active.length === 0) {
|
||||
if (serverFiltered) restoreOriginal();
|
||||
return;
|
||||
}
|
||||
if (!originalData) originalData = [...data()]; // snapshot pagina 1 originale
|
||||
|
||||
const seq = ++filterReqSeq;
|
||||
try {
|
||||
const resp = await fetch("filter_records.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
template_id: parseInt(TEMPLATE_ID, 10),
|
||||
all_users: SHOW_ALL,
|
||||
importref: IMPORTREF,
|
||||
status: "i",
|
||||
filters: active,
|
||||
page: Math.max(1, page),
|
||||
per_page: FILTER_PER_PAGE,
|
||||
}),
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (seq !== filterReqSeq) return; // risposta obsoleta: ignora
|
||||
if (!json.success) {
|
||||
console.error("[gridFilter] filtro:", json.message);
|
||||
return;
|
||||
}
|
||||
serverFiltered = true;
|
||||
showingSelection = false;
|
||||
pagerFn = applyServerFilter;
|
||||
filterPage = json.page;
|
||||
filterTotalPages = json.total_pages;
|
||||
filterTotal = json.total;
|
||||
swapData(json.rows || []);
|
||||
updateFilterPager();
|
||||
hidePagination(true);
|
||||
updateToolbar();
|
||||
} catch (e) {
|
||||
console.error("[gridFilter] fetch filtro fallita", e);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mostra i SELEZIONATI (unione tra filtri diversi) ──────────────────
|
||||
// La selezione si accumula tra filtri; questo carica dal server TUTTI i record
|
||||
// selezionati (per id) e li mostra, paginati, a prescindere dai filtri correnti.
|
||||
async function showSelectedRecords(page = 1) {
|
||||
const ids = [...selected];
|
||||
if (!ids.length) {
|
||||
showingSelection = false;
|
||||
updateToolbar();
|
||||
return;
|
||||
}
|
||||
pagerFn = showSelectedRecords;
|
||||
try {
|
||||
const resp = await fetch("filter_records.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
template_id: parseInt(TEMPLATE_ID, 10),
|
||||
all_users: SHOW_ALL,
|
||||
importref: IMPORTREF,
|
||||
status: "i",
|
||||
ids: ids,
|
||||
page: Math.max(1, page),
|
||||
per_page: FILTER_PER_PAGE,
|
||||
}),
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (!json.success) {
|
||||
console.error("[gridFilter] mostra selezionati:", json.message);
|
||||
return;
|
||||
}
|
||||
if (!originalData) originalData = [...data()];
|
||||
showingSelection = true;
|
||||
serverFiltered = true;
|
||||
filterPage = json.page;
|
||||
filterTotalPages = json.total_pages;
|
||||
filterTotal = json.total;
|
||||
swapData(json.rows || []);
|
||||
updateFilterPager();
|
||||
hidePagination(true);
|
||||
updateToolbar();
|
||||
} catch (e) {
|
||||
console.error("[gridFilter] mostra selezionati fallita", e);
|
||||
}
|
||||
}
|
||||
|
||||
// Tumbler "Mostra selezionati" / "Torna al filtro".
|
||||
function toggleShowSelected() {
|
||||
if (!showingSelection) {
|
||||
if (!selected.size) return;
|
||||
showSelectedRecords(1);
|
||||
} else {
|
||||
exitSelectionView();
|
||||
}
|
||||
}
|
||||
|
||||
// Torna dal "mostra selezionati" al filtro corrente (o alla pagina originale).
|
||||
function exitSelectionView() {
|
||||
showingSelection = false;
|
||||
pagerFn = applyServerFilter;
|
||||
if (collectActiveFilters().length) {
|
||||
applyServerFilter(1);
|
||||
} else {
|
||||
restoreOriginal();
|
||||
}
|
||||
updateToolbar();
|
||||
}
|
||||
|
||||
// Deseleziona tutto.
|
||||
function clearSelection() {
|
||||
selected.clear();
|
||||
document
|
||||
.querySelectorAll("#gridRowContainer .grid-row.row-selected")
|
||||
.forEach((el) => el.classList.remove("row-selected"));
|
||||
document
|
||||
.querySelectorAll(".filter-row-checkbox")
|
||||
.forEach((cb) => (cb.checked = false));
|
||||
const sa = document.getElementById("filterSelectAll");
|
||||
if (sa) sa.checked = false;
|
||||
if (showingSelection) {
|
||||
exitSelectionView();
|
||||
} else {
|
||||
updateToolbar();
|
||||
}
|
||||
}
|
||||
|
||||
// "Seleziona tutti i filtrati": chiede al server TUTTI gli id del match e li seleziona.
|
||||
async function selectAllMatching(on) {
|
||||
if (!serverFiltered) return;
|
||||
const active = collectActiveFilters();
|
||||
try {
|
||||
const resp = await fetch("filter_records.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
template_id: parseInt(TEMPLATE_ID, 10),
|
||||
all_users: SHOW_ALL,
|
||||
importref: IMPORTREF,
|
||||
status: "i",
|
||||
filters: active,
|
||||
page: filterPage,
|
||||
per_page: FILTER_PER_PAGE,
|
||||
want_all_ids: true,
|
||||
}),
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (!json.success) return;
|
||||
(json.all_ids || []).forEach((id) => (on ? selected.add(id) : selected.delete(id)));
|
||||
document
|
||||
.querySelectorAll("#gridRowContainer .grid-row")
|
||||
.forEach((rowEl) => {
|
||||
rowEl.classList.toggle("row-selected", on);
|
||||
const cb = rowEl.querySelector(".filter-row-checkbox");
|
||||
if (cb) cb.checked = on;
|
||||
});
|
||||
updateToolbar();
|
||||
if (json.all_ids_capped) {
|
||||
alert("Selezione limitata a " + (json.all_ids || []).length + " record.");
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[gridFilter] select-all fallita", e);
|
||||
}
|
||||
}
|
||||
|
||||
function syncFilterRow() {
|
||||
const fr = document.getElementById("gridFilterRow");
|
||||
const header = document.getElementById("gridHeaderContainer");
|
||||
if (!fr || !header) return;
|
||||
|
||||
const actHeader = header.querySelector(".button-header");
|
||||
const actCell = fr.querySelector(".filter-actions-cell");
|
||||
if (actHeader && actCell) {
|
||||
const aw = actHeader.offsetWidth;
|
||||
actCell.style.flex = `0 0 ${aw}px`;
|
||||
actCell.style.minWidth = `${aw}px`;
|
||||
}
|
||||
|
||||
const headerCells = header.querySelectorAll(
|
||||
".grid-header:not(.button-header)",
|
||||
);
|
||||
const filterCells = fr.querySelectorAll(
|
||||
".grid-cell:not(.filter-actions-cell)",
|
||||
);
|
||||
headerCells.forEach((hc, i) => {
|
||||
const fc = filterCells[i];
|
||||
if (fc) fc.style.flex = `0 0 ${hc.offsetWidth}px`;
|
||||
});
|
||||
}
|
||||
|
||||
function syncStickyLeft() {
|
||||
const btnHeader = document.querySelector(
|
||||
"#gridHeaderContainer .button-header",
|
||||
);
|
||||
if (!btnHeader) return;
|
||||
const w1 = btnHeader.offsetWidth; // larghezza reale colonna Actions
|
||||
|
||||
const fixRow = (rowEl) => {
|
||||
if (!rowEl) return;
|
||||
const cells = rowEl.querySelectorAll(
|
||||
":scope > .grid-cell, :scope > .grid-header",
|
||||
);
|
||||
const c2 = cells[1];
|
||||
const c3 = cells[2];
|
||||
if (c2 && getComputedStyle(c2).position === "sticky") {
|
||||
c2.style.left = `${w1}px`;
|
||||
}
|
||||
if (c3 && getComputedStyle(c3).position === "sticky") {
|
||||
c3.style.left = `${w1 + (c2 ? c2.offsetWidth : 0)}px`;
|
||||
}
|
||||
};
|
||||
|
||||
fixRow(document.getElementById("gridHeaderContainer"));
|
||||
document.querySelectorAll(".grid-top").forEach(fixRow);
|
||||
document
|
||||
.querySelectorAll("#gridRowContainer .grid-row")
|
||||
.forEach(fixRow);
|
||||
fixRow(document.getElementById("gridFilterRow"));
|
||||
}
|
||||
|
||||
let _headerObs = null;
|
||||
let _rowObs = null;
|
||||
let _syncRaf = null;
|
||||
|
||||
function reconnectObservers() {
|
||||
const header = document.getElementById("gridHeaderContainer");
|
||||
const rows = document.getElementById("gridRowContainer");
|
||||
if (_headerObs && header)
|
||||
_headerObs.observe(header, {
|
||||
attributes: true,
|
||||
attributeFilter: ["style"],
|
||||
subtree: true,
|
||||
});
|
||||
if (_rowObs && rows) _rowObs.observe(rows, { childList: true });
|
||||
}
|
||||
|
||||
function scheduleSync() {
|
||||
if (_syncRaf) return;
|
||||
_syncRaf = requestAnimationFrame(() => {
|
||||
_syncRaf = null;
|
||||
if (_headerObs) _headerObs.disconnect();
|
||||
if (_rowObs) _rowObs.disconnect();
|
||||
syncStickyLeft();
|
||||
syncFilterRow();
|
||||
reconnectObservers();
|
||||
});
|
||||
}
|
||||
|
||||
function hookGridSync() {
|
||||
_headerObs = new MutationObserver(scheduleSync);
|
||||
_rowObs = new MutationObserver(scheduleSync);
|
||||
reconnectObservers();
|
||||
scheduleSync(); // primo allineamento
|
||||
}
|
||||
|
||||
function rowMatchesFilters(row) {
|
||||
if (serverFiltered) return true;
|
||||
for (const [key, term] of Object.entries(colFilters)) {
|
||||
if (!term) continue;
|
||||
const t = term.toLowerCase();
|
||||
const val = getRowColValue(row, key);
|
||||
if (!String(val).toLowerCase().includes(t)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function getRowColValue(row, key) {
|
||||
const cols = meta().columns || [];
|
||||
const col = cols.find((c) => String(c.key) === String(key));
|
||||
if (!col) return "";
|
||||
switch (col.type) {
|
||||
case "detail":
|
||||
case "main_field":
|
||||
return row.details?.[String(key)] ?? "";
|
||||
case "fixed":
|
||||
return row.fixedFields?.[key] ?? "";
|
||||
case "idclient":
|
||||
return row.idclient ?? "";
|
||||
case "cliente_fornitore_id":
|
||||
return row.cliente_fornitore_id ?? "";
|
||||
case "tested_component":
|
||||
return row.tested_component ?? "";
|
||||
case "static":
|
||||
return row[key] ?? "";
|
||||
case "status":
|
||||
return row.status ?? "";
|
||||
default:
|
||||
return row[key] ?? "";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Applica visibilità righe (filtri + restrizione) ──
|
||||
function applyVisibility() {
|
||||
const rows = document.querySelectorAll("#gridRowContainer .grid-row");
|
||||
rows.forEach((rowEl) => {
|
||||
const id = parseInt(rowEl.dataset.id, 10);
|
||||
const row = data().find((r) => String(r.iddatadb) === String(id));
|
||||
let show = true;
|
||||
if (row && !rowMatchesFilters(row)) show = false;
|
||||
if (restricted && restrictedIds && !restrictedIds.has(id))
|
||||
show = false;
|
||||
rowEl.style.display = show ? "" : "none";
|
||||
});
|
||||
window.dispatchEvent(new Event("resize")); // ricalcola scrollbar top
|
||||
}
|
||||
|
||||
// ── Lista iddatadb attualmente visibili (per propagazione/export ecc.) ──
|
||||
window.getVisibleGridIds = function () {
|
||||
if (serverFiltered) {
|
||||
return data()
|
||||
.map((r) => parseInt(r.iddatadb, 10))
|
||||
.filter(Boolean);
|
||||
}
|
||||
const ids = [];
|
||||
document
|
||||
.querySelectorAll("#gridRowContainer .grid-row")
|
||||
.forEach((el) => {
|
||||
if (el.style.display !== "none")
|
||||
ids.push(parseInt(el.dataset.id, 10));
|
||||
});
|
||||
return ids.filter(Boolean);
|
||||
};
|
||||
|
||||
// ── Righe target per operazioni bulk (propaga/export/clona/save) ──
|
||||
// Regola: filtri attivi + selezione → solo selezionate;
|
||||
// filtri attivi senza selezione → solo visibili;
|
||||
// filtri non attivi → null (= tutte, comportamento originale).
|
||||
window.getTargetGridIds = function () {
|
||||
if (!filtersActive) return null; // null = nessun vincolo
|
||||
if (selected.size > 0) return new Set([...selected]);
|
||||
return new Set(window.getVisibleGridIds());
|
||||
};
|
||||
|
||||
// Helper: true se l'id è tra i target (o se non c'è vincolo)
|
||||
window.isTargetGridId = function (id) {
|
||||
const t = window.getTargetGridIds();
|
||||
if (t === null) return true;
|
||||
return t.has(parseInt(id, 10));
|
||||
};
|
||||
|
||||
// ── Toggle selezione riga ──
|
||||
function toggleRow(id, on) {
|
||||
id = parseInt(id, 10);
|
||||
if (on) selected.add(id);
|
||||
else selected.delete(id);
|
||||
const rowEl = document.querySelector(
|
||||
`#gridRowContainer .grid-row[data-id="${id}"]`,
|
||||
);
|
||||
if (rowEl) {
|
||||
rowEl.classList.toggle("row-selected", selected.has(id));
|
||||
const cb = rowEl.querySelector(".filter-row-checkbox");
|
||||
if (cb) cb.checked = selected.has(id);
|
||||
}
|
||||
updateToolbar();
|
||||
}
|
||||
|
||||
// ── Inietta checkbox nelle righe visibili ──
|
||||
// ── Inietta checkbox dentro la cella Actions (già sticky) ──
|
||||
function injectCheckboxes() {
|
||||
document
|
||||
.querySelectorAll("#gridRowContainer .grid-row")
|
||||
.forEach((rowEl) => {
|
||||
const btnCell = rowEl.querySelector(".button-cell");
|
||||
if (!btnCell) return;
|
||||
if (btnCell.querySelector(".filter-row-checkbox")) return;
|
||||
const id = parseInt(rowEl.dataset.id, 10);
|
||||
const wrap = document.createElement("label");
|
||||
wrap.className = "filter-cb-wrap";
|
||||
wrap.innerHTML = `<input type="checkbox" class="filter-row-checkbox" ${selected.has(id) ? "checked" : ""}>`;
|
||||
btnCell.insertBefore(wrap, btnCell.firstChild);
|
||||
if (selected.has(id)) rowEl.classList.add("row-selected");
|
||||
});
|
||||
}
|
||||
|
||||
function removeCheckboxes() {
|
||||
document
|
||||
.querySelectorAll(".filter-cb-wrap")
|
||||
.forEach((el) => el.remove());
|
||||
document
|
||||
.querySelectorAll(".grid-row.row-selected")
|
||||
.forEach((el) => el.classList.remove("row-selected"));
|
||||
}
|
||||
|
||||
// ── Riga input filtri colonna (allineata alle larghezze reali) ──
|
||||
// ── Riga input filtri colonna (allineata alle larghezze reali) ──
|
||||
function injectFilterRow() {
|
||||
if (document.getElementById("gridFilterRow")) return;
|
||||
const top = document.getElementById("gridTopContainer");
|
||||
const header = document.getElementById("gridHeaderContainer");
|
||||
if (!top || !header) return;
|
||||
|
||||
const fr = document.createElement("div");
|
||||
fr.className = "grid-row grid-filter-row";
|
||||
fr.id = "gridFilterRow";
|
||||
|
||||
// cella allineata alla colonna Actions (sticky, legge larghezza reale)
|
||||
const actHeader = header.querySelector(".button-header");
|
||||
const actCell = document.createElement("div");
|
||||
actCell.className = "grid-cell button-cell filter-actions-cell";
|
||||
const aw = actHeader ? actHeader.offsetWidth : 220;
|
||||
actCell.style.flex = `0 0 ${aw}px`;
|
||||
actCell.style.minWidth = `${aw}px`;
|
||||
actCell.innerHTML = `<label class="filter-cb-wrap" style="display:inline-flex;" title="Seleziona tutti i filtrati"><input type="checkbox" id="filterSelectAll"></label>`;
|
||||
fr.appendChild(actCell);
|
||||
|
||||
// una cella per colonna, larghezza letta dall'header reale
|
||||
const headerCells = header.querySelectorAll(
|
||||
".grid-header:not(.button-header)",
|
||||
);
|
||||
(meta().columns || []).forEach((col, i) => {
|
||||
const hc = headerCells[i];
|
||||
const w = hc ? hc.offsetWidth : col.width || 150;
|
||||
const cell = document.createElement("div");
|
||||
cell.className = "grid-cell";
|
||||
cell.style.flex = `0 0 ${w}px`;
|
||||
if (col.type === "tracking" || col.type === "awb") {
|
||||
// niente filtro
|
||||
} else if (col.type === "status") {
|
||||
// Status: dropdown (3 valori) invece del testo libero.
|
||||
const cur = colFilters[col.key] || "";
|
||||
const opt = (v, lbl) =>
|
||||
`<option value="${v}" ${cur === v ? "selected" : ""}>${lbl}</option>`;
|
||||
cell.innerHTML =
|
||||
`<select class="filter-col-input" data-col-key="${col.key}" style="width:100%;padding:3px 6px;font-size:12px;border:1px solid #ced4da;border-radius:4px;">` +
|
||||
opt("", "— tutti —") +
|
||||
opt("i", "Imported") +
|
||||
opt("P", "In Progress") +
|
||||
opt("l", "To LIMS") +
|
||||
`</select>`;
|
||||
} else {
|
||||
cell.innerHTML = `<input type="text" class="filter-col-input" data-col-key="${col.key}" placeholder="🔍 ${col.label || ""}" value="${colFilters[col.key] || ""}" style="width:100%;padding:3px 6px;font-size:12px;border:1px solid #ced4da;border-radius:4px;">`;
|
||||
}
|
||||
fr.appendChild(cell);
|
||||
});
|
||||
|
||||
top.parentNode.insertBefore(fr, top);
|
||||
scheduleSync(); // allinea larghezze filtri + offset sticky
|
||||
}
|
||||
|
||||
function removeFilterRow() {
|
||||
const fr = document.getElementById("gridFilterRow");
|
||||
if (fr) fr.remove();
|
||||
}
|
||||
|
||||
// ── Toolbar (bottoni contestuali) ──
|
||||
function updateToolbar() {
|
||||
const bar = document.getElementById("filterActionBar");
|
||||
if (!bar) return;
|
||||
const count = selected.size;
|
||||
bar.style.display = filtersActive ? "inline-flex" : "none";
|
||||
const cntEl = document.getElementById("filterSelCount");
|
||||
if (cntEl) cntEl.textContent = count;
|
||||
document.getElementById("filterDeleteBtn").disabled = count === 0;
|
||||
|
||||
const showBtn = document.getElementById("filterRestrictBtn");
|
||||
showBtn.disabled = count === 0 && !showingSelection;
|
||||
showBtn.innerHTML = showingSelection
|
||||
? '<i class="fas fa-eye"></i> Torna al filtro'
|
||||
: '<i class="fas fa-list-check"></i> Mostra selezionati';
|
||||
|
||||
const clearSelBtn = document.getElementById("filterClearSelBtn");
|
||||
if (clearSelBtn) clearSelBtn.disabled = count === 0;
|
||||
}
|
||||
|
||||
// ── Main toggle ──
|
||||
function toggleFilters() {
|
||||
filtersActive = !filtersActive;
|
||||
const grid = document.getElementById("gridContainer");
|
||||
const btn = document.getElementById("filtersToggleBtn");
|
||||
if (filtersActive) {
|
||||
grid.classList.add("filters-on");
|
||||
btn.classList.add("active");
|
||||
injectCheckboxes();
|
||||
injectFilterRow();
|
||||
} else {
|
||||
grid.classList.remove("filters-on");
|
||||
btn.classList.remove("active");
|
||||
removeCheckboxes();
|
||||
removeFilterRow();
|
||||
// reset filtri colonna + ripristina la pagina originale
|
||||
Object.keys(colFilters).forEach((k) => delete colFilters[k]);
|
||||
if (serverFiltered) restoreOriginal();
|
||||
}
|
||||
updateToolbar();
|
||||
}
|
||||
|
||||
// ── Batch delete ──
|
||||
async function batchDelete() {
|
||||
const ids = [...selected];
|
||||
if (!ids.length) return;
|
||||
if (
|
||||
!confirm(
|
||||
`Eliminare ${ids.length} righe dal database? Operazione irreversibile.`,
|
||||
)
|
||||
)
|
||||
return;
|
||||
|
||||
const btn = document.getElementById("filterDeleteBtn");
|
||||
btn.disabled = true;
|
||||
const original = btn.innerHTML;
|
||||
|
||||
let ok = 0,
|
||||
fail = 0;
|
||||
for (const id of ids) {
|
||||
try {
|
||||
btn.innerHTML = `<i class="fas fa-spinner fa-spin"></i> ${ok + fail + 1}/${ids.length}`;
|
||||
const resp = await fetch("delete_record.php", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id: id }),
|
||||
});
|
||||
const json = await resp.json();
|
||||
if (json.success) {
|
||||
ok++;
|
||||
// rimuovi da gridData
|
||||
const arr = data();
|
||||
const idx = arr.findIndex(
|
||||
(r) => String(r.iddatadb) === String(id),
|
||||
);
|
||||
if (idx !== -1) arr.splice(idx, 1);
|
||||
selected.delete(id);
|
||||
if (restrictedIds) restrictedIds.delete(id);
|
||||
} else fail++;
|
||||
} catch (e) {
|
||||
fail++;
|
||||
}
|
||||
}
|
||||
|
||||
btn.innerHTML = original;
|
||||
R().renderVisibleRows();
|
||||
if (filtersActive) {
|
||||
injectCheckboxes();
|
||||
}
|
||||
applyVisibility();
|
||||
updateToolbar();
|
||||
alert(`Eliminate: ${ok}${fail ? " — Falliti: " + fail : ""}`);
|
||||
}
|
||||
|
||||
// ── Restringi / mostra tutti ──
|
||||
function toggleRestrict() {
|
||||
if (!restricted) {
|
||||
if (!selected.size) return;
|
||||
restrictedIds = new Set([...selected]);
|
||||
restricted = true;
|
||||
} else {
|
||||
restricted = false;
|
||||
restrictedIds = null;
|
||||
}
|
||||
applyVisibility();
|
||||
updateToolbar();
|
||||
}
|
||||
|
||||
// ── Costruzione UI toolbar (bottoni in cima) ──
|
||||
function buildUI() {
|
||||
const addBtn = document.getElementById("addRowBtn");
|
||||
if (!addBtn) return;
|
||||
|
||||
// bottone Filters
|
||||
const fBtn = document.createElement("button");
|
||||
fBtn.type = "button";
|
||||
fBtn.id = "filtersToggleBtn";
|
||||
fBtn.className = "btn btn-outline-info btn-sm";
|
||||
fBtn.style.flexShrink = "0";
|
||||
fBtn.innerHTML = '<i class="fas fa-filter"></i> Filters';
|
||||
addBtn.parentNode.insertBefore(fBtn, addBtn.nextSibling);
|
||||
|
||||
// barra azioni contestuali
|
||||
const bar = document.createElement("span");
|
||||
bar.id = "filterActionBar";
|
||||
bar.style.cssText =
|
||||
"display:none;align-items:center;gap:8px;flex-shrink:0;";
|
||||
bar.innerHTML = `
|
||||
<span id="filterPager" style="display:none;align-items:center;gap:6px;"></span>
|
||||
<button type="button" id="filterClearBtn" class="btn btn-outline-secondary btn-sm"><i class="fas fa-times"></i> Pulisci filtri</button>
|
||||
<span style="font-size:12px;color:#555;">Selezionate: <strong id="filterSelCount">0</strong></span>
|
||||
<button type="button" id="filterRestrictBtn" class="btn btn-outline-primary btn-sm"><i class="fas fa-list-check"></i> Mostra selezionati</button>
|
||||
<button type="button" id="filterClearSelBtn" class="btn btn-outline-secondary btn-sm"><i class="fas fa-square"></i> Deseleziona</button>
|
||||
<button type="button" id="filterDeleteBtn" class="btn btn-outline-danger btn-sm"><i class="fas fa-trash"></i> Elimina selezionati</button>
|
||||
`;
|
||||
fBtn.parentNode.insertBefore(bar, fBtn.nextSibling);
|
||||
|
||||
fBtn.addEventListener("click", toggleFilters);
|
||||
document
|
||||
.getElementById("filterClearBtn")
|
||||
.addEventListener("click", clearFilters);
|
||||
document
|
||||
.getElementById("filterDeleteBtn")
|
||||
.addEventListener("click", batchDelete);
|
||||
document
|
||||
.getElementById("filterRestrictBtn")
|
||||
.addEventListener("click", toggleShowSelected);
|
||||
document
|
||||
.getElementById("filterClearSelBtn")
|
||||
.addEventListener("click", clearSelection);
|
||||
}
|
||||
|
||||
// Svuota tutti i filtri di colonna (input + select) e torna alla pagina originale.
|
||||
function clearFilters() {
|
||||
Object.keys(colFilters).forEach((k) => delete colFilters[k]);
|
||||
document
|
||||
.querySelectorAll("#gridFilterRow .filter-col-input")
|
||||
.forEach((el) => {
|
||||
el.value = "";
|
||||
});
|
||||
if (serverFiltered) restoreOriginal();
|
||||
}
|
||||
|
||||
// ── Event delegation ──
|
||||
function attachEvents() {
|
||||
// checkbox click
|
||||
document.addEventListener("change", function (e) {
|
||||
if (e.target.classList.contains("filter-row-checkbox")) {
|
||||
const rowEl = e.target.closest(".grid-row");
|
||||
toggleRow(rowEl.dataset.id, e.target.checked);
|
||||
}
|
||||
});
|
||||
|
||||
// Seleziona / deseleziona tutti i FILTRATI
|
||||
document.addEventListener("change", function (e) {
|
||||
if (e.target.id !== "filterSelectAll") return;
|
||||
const on = e.target.checked;
|
||||
|
||||
// Con filtro server-side: seleziona TUTTI i match (anche altre pagine).
|
||||
if (serverFiltered) {
|
||||
selectAllMatching(on);
|
||||
return;
|
||||
}
|
||||
|
||||
document
|
||||
.querySelectorAll("#gridRowContainer .grid-row")
|
||||
.forEach((rowEl) => {
|
||||
if (rowEl.style.display === "none") return;
|
||||
const id = parseInt(rowEl.dataset.id, 10);
|
||||
if (!id) return;
|
||||
if (on) selected.add(id);
|
||||
else selected.delete(id);
|
||||
rowEl.classList.toggle("row-selected", on);
|
||||
const cb = rowEl.querySelector(".filter-row-checkbox");
|
||||
if (cb) cb.checked = on;
|
||||
});
|
||||
updateToolbar();
|
||||
});
|
||||
|
||||
// click su QUALSIASI punto della riga (tranne campi interattivi) = seleziona
|
||||
document.addEventListener("click", function (e) {
|
||||
if (!filtersActive) return;
|
||||
const rowEl = e.target.closest("#gridRowContainer .grid-row");
|
||||
if (!rowEl) return;
|
||||
|
||||
// ignora click su elementi interattivi
|
||||
if (
|
||||
e.target.closest(
|
||||
"input, select, textarea, button, a, .select2-container, .action-btn, .propagate-btn, .add-part-btn",
|
||||
)
|
||||
) {
|
||||
// ma se ho cliccato proprio la checkbox, lascia fare al change
|
||||
return;
|
||||
}
|
||||
|
||||
const cb = rowEl.querySelector(".filter-row-checkbox");
|
||||
if (!cb) return;
|
||||
cb.checked = !cb.checked;
|
||||
toggleRow(rowEl.dataset.id, cb.checked);
|
||||
});
|
||||
|
||||
// filtri colonna testo (debounce) → filtro server-side su tutto il template
|
||||
let t = null;
|
||||
document.addEventListener("input", function (e) {
|
||||
if (!e.target.classList.contains("filter-col-input")) return;
|
||||
if (e.target.tagName === "SELECT") return; // i select li gestisce 'change'
|
||||
const key = e.target.dataset.colKey;
|
||||
colFilters[key] = e.target.value;
|
||||
clearTimeout(t);
|
||||
t = setTimeout(applyServerFilter, 250);
|
||||
});
|
||||
|
||||
// dropdown (es. Status): applica subito, senza debounce
|
||||
document.addEventListener("change", function (e) {
|
||||
if (!e.target.classList.contains("filter-col-input")) return;
|
||||
if (e.target.tagName !== "SELECT") return;
|
||||
colFilters[e.target.dataset.colKey] = e.target.value;
|
||||
applyServerFilter();
|
||||
});
|
||||
}
|
||||
|
||||
// ── Ripristina stato filtri dopo un re-render (checkbox + restrizione) ──
|
||||
let _restoring = false;
|
||||
function restoreFilterState() {
|
||||
if (!filtersActive || _restoring) return;
|
||||
_restoring = true; // evita loop con l'observer mentre iniettiamo/nascondiamo
|
||||
try {
|
||||
injectCheckboxes();
|
||||
applyVisibility(); // ri-applica filtri colonna E restringi selezione
|
||||
} finally {
|
||||
// rilascia dopo il tick così le nostre mutazioni non ci ri-triggerano subito
|
||||
setTimeout(() => {
|
||||
_restoring = false;
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Observer: qualunque cosa ricrei le righe (propagazione, save, ecc.)
|
||||
// ripristina automaticamente checkbox + restrizione. Robusto perché
|
||||
// non dipende da chi chiama renderVisibleRows. ──
|
||||
function hookRerender() {
|
||||
const rowC = document.getElementById("gridRowContainer");
|
||||
if (!rowC) return;
|
||||
|
||||
const obs = new MutationObserver(() => {
|
||||
if (!filtersActive || _restoring) return;
|
||||
// debounce leggero: aspetta che il render finisca
|
||||
clearTimeout(hookRerender._t);
|
||||
hookRerender._t = setTimeout(restoreFilterState, 30);
|
||||
});
|
||||
|
||||
obs.observe(rowC, { childList: true });
|
||||
}
|
||||
|
||||
function init() {
|
||||
if (!window.gridRenderer) {
|
||||
console.error("[gridFilter] gridRenderer non trovato");
|
||||
return;
|
||||
}
|
||||
buildUI();
|
||||
attachEvents();
|
||||
hookRerender();
|
||||
hookGridSync();
|
||||
}
|
||||
|
||||
// parte dopo che gridRenderer ha finito init
|
||||
if (document.readyState === "loading") {
|
||||
document.addEventListener("DOMContentLoaded", () =>
|
||||
setTimeout(init, 300),
|
||||
);
|
||||
} else {
|
||||
setTimeout(init, 300);
|
||||
}
|
||||
})();
|
||||
File diff suppressed because it is too large
Load Diff
@@ -59,8 +59,7 @@ foreach ($allMappings as $mapping) {
|
||||
}
|
||||
}
|
||||
if (!$mainFieldMapping) {
|
||||
$filtered = array_filter($allMappings, fn($m) => !$m['is_manual']);
|
||||
$mainFieldMapping = reset($filtered);
|
||||
$mainFieldMapping = reset(array_filter($allMappings, fn($m) => !$m['is_manual']));
|
||||
}
|
||||
|
||||
// Retrieve data from datadb
|
||||
@@ -663,11 +662,6 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
} else {
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
}
|
||||
// Status (subito dopo main_field)
|
||||
$fixedColumnsReduced = ['status'];
|
||||
foreach ($fixedColumnsReduced as $col) {
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
}
|
||||
// Campi automatici (escluso main_field)
|
||||
$autoIndex = ($mainFieldMapping && !$mainFieldMapping['is_manual']) ? 1 : 0;
|
||||
foreach ($allMappings as $mapping) {
|
||||
@@ -718,7 +712,11 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$manualIndex++;
|
||||
}
|
||||
}
|
||||
// Colonne Import Reference Code, filename_import
|
||||
// Colonne status, Import Reference Code, filename_import
|
||||
$fixedColumnsReduced = ['status'];
|
||||
foreach ($fixedColumnsReduced as $col) {
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>";
|
||||
}
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // Import Reference Code
|
||||
echo "<div class='grid-cell' style='flex: 0 0 150px;'></div>"; // filename_import
|
||||
// AWB Number e Tracking Info
|
||||
@@ -736,12 +734,6 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>" . htmlspecialchars($mainFieldMapping['field_label']) . "<div class='resizer'></div></div>";
|
||||
$headerIndex++;
|
||||
}
|
||||
// Header per status (subito dopo main_field)
|
||||
foreach ($fixedColumnsReduced as $col) {
|
||||
$displayName = $slugMapping[$col] ?? $col;
|
||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>$displayName<div class='resizer'></div></div>";
|
||||
$headerIndex++;
|
||||
}
|
||||
// Header per campi automatici (escluso main_field)
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (!$mapping['is_manual'] && $mapping['main_field'] != 1) {
|
||||
@@ -756,7 +748,12 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$headerIndex++;
|
||||
}
|
||||
}
|
||||
// Header per Import Reference Code, filename_import
|
||||
// Header per status, Import Reference Code, filename_import
|
||||
foreach ($fixedColumnsReduced as $col) {
|
||||
$displayName = $slugMapping[$col] ?? $col;
|
||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>$displayName<div class='resizer'></div></div>";
|
||||
$headerIndex++;
|
||||
}
|
||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>Import Reference Code<div class='resizer'></div></div>";
|
||||
$headerIndex++;
|
||||
echo "<div class='grid-header' data-index='$headerIndex' style='flex: 0 0 150px; position: relative;'>File<div class='resizer'></div></div>";
|
||||
@@ -773,11 +770,13 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
<div style="display: flex; gap: 5px; justify-content: center;">
|
||||
<?php if (!$is_readonly): ?>
|
||||
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" style="background: #28a745; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-save"></i></button>
|
||||
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-camera"></i></button>
|
||||
<button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ffc107; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-puzzle-piece"></i></button>
|
||||
<?php else: ?>
|
||||
<button type="button" class="save-btn action-btn" data-row="<?= $index ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; flex: 1;" disabled><i class="fas fa-save"></i></button>
|
||||
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; flex: 1;" disabled><i class="fas fa-camera"></i></button>
|
||||
<button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ccc; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: not-allowed; flex: 1;" disabled><i class="fas fa-puzzle-piece"></i></button>
|
||||
<?php endif; ?>
|
||||
<button type="button" class="photos-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #007bff; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-camera"></i></button>
|
||||
<button type="button" class="parts-btn action-btn" data-row="<?= $index ?>" data-iddatadb="<?= $row['iddatadb'] ?>" style="background: #ffc107; color: white; border: none; padding: 8px 12px; border-radius: 5px; cursor: pointer; flex: 1;"><i class="fas fa-puzzle-piece"></i></button>
|
||||
</div>
|
||||
</div>
|
||||
<?php
|
||||
@@ -807,25 +806,6 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
echo "</div>";
|
||||
$cellIndex++;
|
||||
}
|
||||
// Status (subito dopo main_field)
|
||||
$fixedColumnsReduced = ['status'];
|
||||
foreach ($fixedColumnsReduced as $col) {
|
||||
$value = $row[$col] ?? '';
|
||||
echo "<div class='grid-cell editable-cell' data-col='$col' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
|
||||
if ($col === 'status') {
|
||||
$badgeClass = $value === 'i' ? 'status-i' : ($value === 'P' ? 'status-P' : 'status-l');
|
||||
$badgeText = $value === 'i' ? 'Imported' : ($value === 'P' ? 'Progress' : 'LIMS');
|
||||
// Aggiungi il numero di commessaweb se lo status è 'l'
|
||||
if ($value === 'l') {
|
||||
$commessaWeb = isset($row['commessaweb']) ? htmlspecialchars($row['commessaweb']) : '';
|
||||
$badgeText .= " ($commessaWeb)";
|
||||
}
|
||||
echo "<span class='status-badge $badgeClass'>" . htmlspecialchars($badgeText) . "</span>";
|
||||
echo "<input type='hidden' name='rows[$index][$col]' value='" . htmlspecialchars($value ?? 'i') . "'>";
|
||||
}
|
||||
echo "</div>";
|
||||
$cellIndex++;
|
||||
}
|
||||
// Campi automatici (escluso main_field)
|
||||
$autoIndex = ($mainFieldMapping && !$mainFieldMapping['is_manual']) ? 1 : 0;
|
||||
foreach ($allMappings as $mapping) {
|
||||
@@ -883,6 +863,20 @@ foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
|
||||
$manualIndex++;
|
||||
}
|
||||
}
|
||||
// Colonna status
|
||||
$fixedColumnsReduced = ['status'];
|
||||
foreach ($fixedColumnsReduced as $col) {
|
||||
$value = $row[$col] ?? '';
|
||||
echo "<div class='grid-cell editable-cell' data-col='$col' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
|
||||
if ($col === 'status') {
|
||||
$badgeClass = $value === 'i' ? 'status-i' : ($value === 'P' ? 'status-P' : 'status-l');
|
||||
$badgeText = $value === 'i' ? 'Imported' : ($value === 'P' ? 'Progress' : 'LIMS');
|
||||
echo "<span class='status-badge $badgeClass'>" . htmlspecialchars($badgeText) . "</span>";
|
||||
echo "<input type='hidden' name='rows[$index][$col]' value='" . htmlspecialchars($value ?? 'i') . "'>";
|
||||
}
|
||||
echo "</div>";
|
||||
$cellIndex++;
|
||||
}
|
||||
// Colonne Import Reference Code e filename_import
|
||||
echo "<div class='grid-cell' data-col='importreferencecode' data-row='$index' data-index='$cellIndex' style='flex: 0 0 150px;'>";
|
||||
echo "<span>" . htmlspecialchars($row['importreferencecode']) . "</span>";
|
||||
|
||||
@@ -10,180 +10,70 @@
|
||||
<title>Template Buttons - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
|
||||
<style>
|
||||
/* Main buttons container */
|
||||
.template-buttons {
|
||||
/* Layout flessibile per gestire dimensioni diverse */
|
||||
#templateButtons {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
gap: 10px;
|
||||
justify-content: flex-start;
|
||||
padding: 10px 0;
|
||||
/* Allinea a sinistra */
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Button sizes */
|
||||
/* Definizione delle dimensioni */
|
||||
/* Definizione delle dimensioni */
|
||||
.btn-small {
|
||||
font-size: 12px;
|
||||
padding: 6px 12px;
|
||||
min-width: 100px;
|
||||
min-height: 36px;
|
||||
display: inline-flex;
|
||||
min-height: 30px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 8px;
|
||||
text-align: center;
|
||||
/* Aggiunto */
|
||||
}
|
||||
|
||||
.btn-medium {
|
||||
font-size: 16px;
|
||||
padding: 10px 20px;
|
||||
min-width: 140px;
|
||||
min-height: 48px;
|
||||
display: inline-flex;
|
||||
min-width: 130px;
|
||||
min-height: 45px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-radius: 10px;
|
||||
text-align: center;
|
||||
/* Aggiunto */
|
||||
}
|
||||
|
||||
.btn-large {
|
||||
font-size: 20px;
|
||||
padding: 14px 28px;
|
||||
min-width: 190px;
|
||||
min-height: 64px;
|
||||
display: inline-flex;
|
||||
min-width: 180px;
|
||||
min-height: 60px;
|
||||
display: flex;
|
||||
/* Aggiunto */
|
||||
justify-content: center;
|
||||
/* Aggiunto */
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
border-radius: 12px;
|
||||
text-align: center;
|
||||
/* Aggiunto */
|
||||
}
|
||||
|
||||
.template-icon {
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.btn-large .template-icon {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.btn-small .template-icon {
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
/* Search box */
|
||||
/* Stile della barra di ricerca */
|
||||
#searchInput {
|
||||
width: 100%;
|
||||
padding: 10px 14px;
|
||||
padding: 10px;
|
||||
font-size: 16px;
|
||||
margin-bottom: 18px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 8px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
#searchInput:focus {
|
||||
border-color: #0d6efd;
|
||||
box-shadow: 0 0 0 0.15rem rgba(13, 110, 253, 0.15);
|
||||
}
|
||||
|
||||
/* Tabs */
|
||||
.custom-tabs {
|
||||
border-bottom: 1px solid #e5e5e5;
|
||||
margin-bottom: 20px;
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
.custom-tabs .nav-link {
|
||||
border: none;
|
||||
border-radius: 10px 10px 0 0;
|
||||
color: #555;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 10px 18px;
|
||||
}
|
||||
|
||||
.custom-tabs .nav-link:hover {
|
||||
background: #f8f9fa;
|
||||
color: #0d6efd;
|
||||
}
|
||||
|
||||
.custom-tabs .nav-link.active {
|
||||
background: #0d6efd;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.tab-pane {
|
||||
min-height: 140px;
|
||||
}
|
||||
|
||||
.empty-message {
|
||||
color: #6c757d;
|
||||
font-style: italic;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.loading-message {
|
||||
color: #6c757d;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #6c757d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 2px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.section-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.template-btn-wrapper {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.favorite-star {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
z-index: 2;
|
||||
transition: color 0.15s, transform 0.15s;
|
||||
}
|
||||
|
||||
.favorite-star:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.favorite-star.active {
|
||||
color: #ffc107;
|
||||
margin-bottom: 15px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php include('include/popup.php'); ?>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
@@ -197,54 +87,14 @@
|
||||
<h6 class="mb-0">Active Templates</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<!-- Barra di ricerca -->
|
||||
<input type="text" id="searchInput" placeholder="Search template...">
|
||||
|
||||
<ul class="nav nav-tabs custom-tabs" id="templateTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="xls-tab" data-bs-toggle="tab" data-bs-target="#xls-pane" type="button" role="tab" aria-controls="xls-pane" aria-selected="true">
|
||||
<i class="bx bx-spreadsheet template-icon"></i>
|
||||
XLS
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="api-tab" data-bs-toggle="tab" data-bs-target="#api-pane" type="button" role="tab" aria-controls="api-pane" aria-selected="false">
|
||||
<i class="bx bx-transfer-alt template-icon"></i>
|
||||
JSON/API
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="pdf-tab" data-bs-toggle="tab" data-bs-target="#pdf-pane" type="button" role="tab" aria-controls="pdf-pane" aria-selected="false">
|
||||
<i class="bx bx-file-pdf template-icon"></i>
|
||||
PDF
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content" id="templateTabsContent">
|
||||
<div class="tab-pane fade show active" id="xls-pane" role="tabpanel" aria-labelledby="xls-tab">
|
||||
<div id="templateButtonsXLS" class="template-buttons">
|
||||
<div class="loading-message">Loading XLS templates...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="api-pane" role="tabpanel" aria-labelledby="api-tab">
|
||||
<div id="templateButtonsAPI" class="template-buttons">
|
||||
<div class="loading-message">Loading API templates...</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="pdf-pane" role="tabpanel" aria-labelledby="pdf-tab">
|
||||
<div id="templateButtonsPDF" class="template-buttons">
|
||||
<div class="loading-message">Loading PDF templates...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="templateButtons"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
@@ -254,214 +104,46 @@
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
const allTemplates = [];
|
||||
|
||||
const containers = {
|
||||
XLS: document.getElementById("templateButtonsXLS"),
|
||||
API: document.getElementById("templateButtonsAPI"),
|
||||
PDF: document.getElementById("templateButtonsPDF")
|
||||
};
|
||||
|
||||
function getSizeClass(buttonSize) {
|
||||
if (buttonSize === "small") return "btn-small";
|
||||
if (buttonSize === "large") return "btn-large";
|
||||
return "btn-medium";
|
||||
}
|
||||
|
||||
function getTemplateIcon(sourceType) {
|
||||
switch ((sourceType || '').toUpperCase()) {
|
||||
case 'XLS':
|
||||
return 'bx bx-spreadsheet';
|
||||
case 'API':
|
||||
return 'bx bx-transfer-alt';
|
||||
case 'PDF':
|
||||
return 'bx bx-file-pdf';
|
||||
default:
|
||||
return 'bx bx-file';
|
||||
}
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function createButtonWrapper(template) {
|
||||
const sizeClass = getSizeClass(template.button_size);
|
||||
const sourceType = (template.source_type || '').toUpperCase();
|
||||
const iconClass = getTemplateIcon(sourceType);
|
||||
const isFavorite = !!parseInt(template.is_favorite);
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "template-btn-wrapper";
|
||||
|
||||
const btn = document.createElement("a");
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
btn.className = `btn ${sizeClass}`;
|
||||
btn.style.backgroundColor = template.button_bg_color || '#0d6efd';
|
||||
btn.style.color = template.button_text_color || '#ffffff';
|
||||
btn.setAttribute("data-label", (template.button_label || '').toLowerCase());
|
||||
btn.setAttribute("data-source-type", sourceType);
|
||||
|
||||
btn.innerHTML = `
|
||||
<i class="${iconClass} template-icon"></i>
|
||||
<span>${escapeHtml(template.button_label || 'Unnamed')}</span>
|
||||
`;
|
||||
|
||||
const star = document.createElement("span");
|
||||
star.className = "favorite-star" + (isFavorite ? " active" : "");
|
||||
star.innerHTML = `<i class="bx ${isFavorite ? 'bxs-star' : 'bx-star'}"></i>`;
|
||||
star.setAttribute("data-template-id", template.id);
|
||||
star.title = isFavorite ? "Rimuovi dai preferiti" : "Aggiungi ai preferiti";
|
||||
|
||||
star.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleFavorite(template.id);
|
||||
});
|
||||
|
||||
wrapper.appendChild(btn);
|
||||
wrapper.appendChild(star);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function toggleFavorite(templateId) {
|
||||
const template = allTemplates.find(t => String(t.id) === String(templateId));
|
||||
if (!template) return;
|
||||
|
||||
const newState = !parseInt(template.is_favorite);
|
||||
|
||||
// Optimistic update
|
||||
template.is_favorite = newState ? 1 : 0;
|
||||
renderTemplates(document.getElementById("searchInput").value.toLowerCase().trim());
|
||||
|
||||
fetch("toggle_template_favorite.php", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: templateId,
|
||||
is_favorite: newState
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("Failed to toggle favorite:", data.message);
|
||||
// Rollback in caso di errore
|
||||
template.is_favorite = newState ? 0 : 1;
|
||||
renderTemplates(document.getElementById("searchInput").value.toLowerCase().trim());
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Fetch error toggling favorite:", error);
|
||||
template.is_favorite = newState ? 0 : 1;
|
||||
renderTemplates(document.getElementById("searchInput").value.toLowerCase().trim());
|
||||
});
|
||||
}
|
||||
|
||||
function clearContainers() {
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = '';
|
||||
});
|
||||
}
|
||||
|
||||
function renderSection(container, title, templates) {
|
||||
if (templates.length === 0) return;
|
||||
|
||||
const heading = document.createElement("div");
|
||||
heading.className = "section-title";
|
||||
heading.textContent = title;
|
||||
container.appendChild(heading);
|
||||
|
||||
const group = document.createElement("div");
|
||||
group.className = "template-buttons";
|
||||
group.style.marginTop = "0";
|
||||
group.style.paddingTop = "0";
|
||||
|
||||
templates.forEach(template => {
|
||||
group.appendChild(createButtonWrapper(template));
|
||||
});
|
||||
|
||||
container.appendChild(group);
|
||||
}
|
||||
|
||||
function renderTemplates(searchValue = '') {
|
||||
clearContainers();
|
||||
|
||||
const grouped = {
|
||||
XLS: [],
|
||||
API: [],
|
||||
PDF: []
|
||||
};
|
||||
|
||||
allTemplates.forEach(template => {
|
||||
const sourceType = (template.source_type || '').toUpperCase();
|
||||
const label = (template.button_label || '').toLowerCase();
|
||||
|
||||
if (searchValue && !label.includes(searchValue)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (grouped[sourceType]) {
|
||||
grouped[sourceType].push(template);
|
||||
}
|
||||
});
|
||||
|
||||
Object.keys(grouped).forEach(type => {
|
||||
const container = containers[type];
|
||||
if (!container) return;
|
||||
|
||||
if (grouped[type].length === 0) {
|
||||
container.innerHTML = `<div class="empty-message">No templates found in this section.</div>`;
|
||||
return;
|
||||
}
|
||||
|
||||
const favorites = grouped[type].filter(t => parseInt(t.is_favorite) === 1);
|
||||
const others = grouped[type].filter(t => parseInt(t.is_favorite) !== 1);
|
||||
|
||||
renderSection(container, "★ Preferiti", favorites);
|
||||
renderSection(container, "Altri", others);
|
||||
});
|
||||
}
|
||||
|
||||
fetch("load_active_templates.php")
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("Error loading templates:", data.message);
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Error loading templates.</div>`;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Array.isArray(data.data)) {
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Invalid response format.</div>`;
|
||||
});
|
||||
return;
|
||||
}
|
||||
let templateButtons = document.getElementById("templateButtons");
|
||||
templateButtons.innerHTML = '';
|
||||
|
||||
allTemplates.push(...data.data);
|
||||
renderTemplates();
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Fetch error:", error);
|
||||
clearContainers();
|
||||
Object.values(containers).forEach(container => {
|
||||
container.innerHTML = `<div class="empty-message">Fetch error while loading templates.</div>`;
|
||||
data.data.forEach(template => {
|
||||
let sizeClass = "btn-medium"; // Default
|
||||
if (template.button_size === "small") sizeClass = "btn-small";
|
||||
if (template.button_size === "large") sizeClass = "btn-large";
|
||||
|
||||
let btn = document.createElement("a");
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
btn.className = `btn ${sizeClass}`;
|
||||
btn.style.backgroundColor = template.button_bg_color;
|
||||
btn.style.color = template.button_text_color;
|
||||
btn.textContent = template.button_label;
|
||||
btn.setAttribute("data-label", template.button_label.toLowerCase()); // Attributo per ricerca
|
||||
|
||||
templateButtons.appendChild(btn);
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(error => console.error("Fetch error:", error));
|
||||
|
||||
// Funzione per la ricerca live
|
||||
document.getElementById("searchInput").addEventListener("input", function() {
|
||||
const searchValue = this.value.toLowerCase().trim();
|
||||
renderTemplates(searchValue);
|
||||
let searchValue = this.value.toLowerCase();
|
||||
document.querySelectorAll("#templateButtons a").forEach(btn => {
|
||||
let label = btn.getAttribute("data-label");
|
||||
if (label.includes(searchValue)) {
|
||||
btn.style.display = "inline-block";
|
||||
} else {
|
||||
btn.style.display = "none";
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
+269
-2285
File diff suppressed because it is too large
Load Diff
@@ -17,51 +17,23 @@ if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !i
|
||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Richiesta non valida"));
|
||||
exit;
|
||||
}
|
||||
|
||||
$template_id = intval($_POST['template_id']);
|
||||
$selected_rows = array_map('intval', $_POST['selected_rows']);
|
||||
$columns = json_decode(urldecode($_POST['columns'] ?? '[]'), true);
|
||||
$rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||
|
||||
$newFilename = $_POST['filename'];
|
||||
$selected_rows = $_POST['selected_rows'];
|
||||
$columns = json_decode($_POST['columns'], true); // Header dell'XLS
|
||||
$rows = json_decode($_POST['rows'], true); // Dati dell'XLS
|
||||
$newFilename = htmlspecialchars($_POST['filename']);
|
||||
|
||||
$_SESSION['template_id'] = $template_id;
|
||||
$_SESSION['selected_rows'] = $selected_rows;
|
||||
$_SESSION['columns'] = $columns;
|
||||
$_SESSION['rows'] = $rows;
|
||||
$_SESSION['excelrows'] = $excelrows;
|
||||
$_SESSION['filename'] = $newFilename;
|
||||
|
||||
error_log("Received Data - Template ID: $template_id, Selected Rows: " . json_encode($selected_rows));
|
||||
error_log("Columns: " . json_encode($columns));
|
||||
error_log("Rows: " . json_encode($rows));
|
||||
error_log("Excelrows: " . json_encode($excelrows));
|
||||
|
||||
$user_id = $iduserlogin ?? 1;
|
||||
|
||||
/**
|
||||
* Normalizza un nome colonna per il confronto header Excel <-> mapping.
|
||||
* Risolve alla radice i mismatch causati da caratteri invisibili:
|
||||
* - non-breaking space (U+00A0), narrow NBSP (U+202F), figure space (U+2007), ecc. -> spazio normale
|
||||
* - tab, newline e altri whitespace -> spazio normale
|
||||
* - spazi multipli consecutivi -> un solo spazio
|
||||
* - trim ai bordi
|
||||
* NB: usata SOLO per il matching, non altera i valori importati.
|
||||
*/
|
||||
function normalizeColName($s): string
|
||||
{
|
||||
$s = (string)$s;
|
||||
// Converte i principali spazi Unicode "insidiosi" in spazio normale
|
||||
$s = str_replace(
|
||||
["\xC2\xA0", "\xE2\x80\xAF", "\xE2\x80\x87", "\xE2\x80\x89", "\xE2\x80\x8A", "\xEF\xBB\xBF"],
|
||||
' ',
|
||||
$s
|
||||
);
|
||||
// Qualsiasi sequenza di whitespace (spazi, tab, newline) -> singolo spazio
|
||||
$s = preg_replace('/\s+/u', ' ', $s);
|
||||
return trim((string)$s);
|
||||
}
|
||||
$user_id = $iduserlogin ?? 1; // Default a 1 se non definito
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
@@ -70,7 +42,7 @@ $pdo = $db->getConnection();
|
||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||
|
||||
// Recupera tutti i mapping dal template
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$template_id]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -88,52 +60,32 @@ foreach ($allMappings as $mapping) {
|
||||
}
|
||||
}
|
||||
|
||||
// Inserisci le righe selezionate in datadb
|
||||
// Inserisci le righe selezionate in datadb (solo campi generici con templateid)
|
||||
$insertedIds = [];
|
||||
foreach ($selected_rows as $rowIndex) {
|
||||
$row = $rows[$rowIndex] ?? null;
|
||||
$excelrow = $excelrows[$rowIndex] ?? null;
|
||||
|
||||
if ($row === null || $excelrow === null) {
|
||||
error_log("Errore: riga o excelrow mancante per rowIndex $rowIndex");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Recupera l'idclient di default dal template
|
||||
$template_stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
|
||||
$template_stmt->execute([$template_id]);
|
||||
$template = $template_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$default_idclient = $template['idclient'] ?? null;
|
||||
|
||||
$row = $rows[$rowIndex];
|
||||
$values = [
|
||||
$template_id,
|
||||
$importReferenceCode,
|
||||
$newFilename,
|
||||
'i',
|
||||
$user_id,
|
||||
null,
|
||||
date('Y-m-d'),
|
||||
$excelrow,
|
||||
$default_idclient // Aggiungi idclient
|
||||
$template_id, // templateid
|
||||
$importReferenceCode, // importreferencecode
|
||||
$newFilename, // filename_import
|
||||
'i', // status
|
||||
$user_id, // user_id
|
||||
null, // limscode
|
||||
date('Y-m-d') // importdate
|
||||
];
|
||||
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow, idclient) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate) VALUES (?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($values);
|
||||
|
||||
$iddatadb = $pdo->lastInsertId();
|
||||
$insertedIds[] = $iddatadb;
|
||||
|
||||
// Inserisci tutti i campi in import_data_details
|
||||
// Inserisci tutti i campi (automatici e manuali) in import_data_details
|
||||
foreach ($allMappings as $mapping) {
|
||||
$fieldValue = null;
|
||||
if (!$mapping['is_manual']) {
|
||||
if (!$mapping['is_manual']) { // Campi automatici dall'XLS
|
||||
$excelColumn = trim($mapping['excel_column']);
|
||||
// Match robusto: normalizza header e mapping (NBSP, spazi doppi, tab...)
|
||||
// così un solo/doppio spazio o un non-breaking space non rompono il confronto.
|
||||
$excelColumnIndex = array_search(
|
||||
normalizeColName($excelColumn),
|
||||
array_map('normalizeColName', $columns)
|
||||
);
|
||||
$excelColumnIndex = array_search($excelColumn, array_map('trim', $columns));
|
||||
if ($excelColumnIndex !== false && isset($row[$excelColumnIndex]) && $row[$excelColumnIndex] !== '') {
|
||||
$fieldValue = $row[$excelColumnIndex];
|
||||
error_log("Found Excel column '$excelColumn' at index $excelColumnIndex, value: " . var_export($fieldValue, true));
|
||||
@@ -154,24 +106,15 @@ foreach ($selected_rows as $rowIndex) {
|
||||
case 'Testo':
|
||||
case 'VARCHAR':
|
||||
default:
|
||||
$fieldValue = !empty($fieldValue) ? (string)$fieldValue : ($mapping['manual_default'] ?? '');
|
||||
$fieldValue = !empty($fieldValue) ? htmlspecialchars((string)$fieldValue) : ($mapping['manual_default'] ?? '');
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
} else { // Campi manuali
|
||||
$fieldValue = $mapping['manual_default'] ?? '';
|
||||
if ($mapping['data_type'] === 'DATE' && $mapping['manual_default'] === 'today') {
|
||||
$fieldValue = date('Y-m-d');
|
||||
}
|
||||
}
|
||||
// Apply auto_value if field is still empty
|
||||
if (($fieldValue === null || $fieldValue === '') && !empty($mapping['auto_value']) && $mapping['auto_value'] !== 'none') {
|
||||
if ($mapping['auto_value'] === 'import_date') {
|
||||
$fieldValue = date('Y-m-d');
|
||||
} elseif ($mapping['auto_value'] === 'import_time') {
|
||||
$fieldValue = date('H:i');
|
||||
}
|
||||
}
|
||||
|
||||
if ($mapping['is_required'] && (is_null($fieldValue) || $fieldValue === '')) {
|
||||
error_log("Required field missing for mapping ID: " . $mapping['id'] . ", field: " . $mapping['field_label']);
|
||||
}
|
||||
@@ -182,60 +125,33 @@ foreach ($selected_rows as $rowIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// AUTO SET: schema field_id = 244 (Accettatore) con auth_users.lims_user_id
|
||||
// Spostato dal vecchio flusso (import_edit.php) al flusso nuovo.
|
||||
// Autocompila il custom field 244 con il lims_user_id dell'utente loggato.
|
||||
// ------------------------------------------------------------
|
||||
try {
|
||||
// 1) Leggi il lims_user_id dell'utente loggato
|
||||
$stmtUser = $pdo->prepare("SELECT lims_user_id FROM auth_users WHERE id = ? LIMIT 1");
|
||||
$stmtUser->execute([(int)$user_id]);
|
||||
$limsUserId = $stmtUser->fetchColumn();
|
||||
|
||||
// normalize
|
||||
$limsUserId = ($limsUserId !== false && $limsUserId !== null && $limsUserId !== '') ? (string)$limsUserId : '';
|
||||
|
||||
if ($limsUserId !== '' && !empty($insertedIds)) {
|
||||
|
||||
// 2) Trova il mapping_id per field_id = 244 in questo template
|
||||
$stmtMap = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ? AND field_id = 244 LIMIT 1");
|
||||
$stmtMap->execute([(int)$template_id]);
|
||||
$mappingId244 = (int)$stmtMap->fetchColumn();
|
||||
|
||||
if ($mappingId244 > 0) {
|
||||
|
||||
// 3) Upsert del valore in import_data_details per ogni riga inserita
|
||||
$pdo->beginTransaction();
|
||||
|
||||
$updStmt = $pdo->prepare("UPDATE import_data_details SET field_value = ? WHERE id = ? AND mapping_id = ?");
|
||||
$insStmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
||||
|
||||
foreach ($insertedIds as $iddatadb) {
|
||||
$iddatadb = (int)$iddatadb;
|
||||
if ($iddatadb <= 0) continue;
|
||||
|
||||
$updStmt->execute([$limsUserId, $iddatadb, $mappingId244]);
|
||||
|
||||
if ($updStmt->rowCount() === 0) {
|
||||
$insStmt->execute([$iddatadb, $mappingId244, $limsUserId]);
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
} else {
|
||||
error_log("[AUTO FIELD 244] Nessun mapping field_id=244 per template $template_id");
|
||||
}
|
||||
} else {
|
||||
error_log("[AUTO FIELD 244] limsUserId vuoto per user_id=$user_id o nessuna riga inserita");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Non bloccare l'import se questo fallisce; logga soltanto
|
||||
error_log("[AUTO FIELD 244] " . $e->getMessage());
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
}
|
||||
|
||||
$_SESSION['inserted_ids'] = $insertedIds;
|
||||
|
||||
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||
$params = [
|
||||
'template_id' => $template_id,
|
||||
'filename' => $newFilename,
|
||||
];
|
||||
|
||||
?>
|
||||
<form id="redirectForm" action="import_edit2.php" method="post">
|
||||
<input type="hidden" name="template_id" value="<?= htmlspecialchars($template_id) ?>">
|
||||
<input type="hidden" name="filename" value="<?= htmlspecialchars($newFilename) ?>">
|
||||
|
||||
<?php foreach ($selected_rows as $row): ?>
|
||||
<input type="hidden" name="selected_rows[]" value="<?= htmlspecialchars($row) ?>">
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($insertedIds as $id): ?>
|
||||
<input type="hidden" name="inserted_ids[]" value="<?= htmlspecialchars($id) ?>">
|
||||
<?php endforeach; ?>
|
||||
|
||||
<input type="hidden" name="columns" value='<?= json_encode($columns) ?>'>
|
||||
<input type="hidden" name="rows" value='<?= json_encode($rows) ?>'>
|
||||
</form>
|
||||
<script>
|
||||
document.getElementById('redirectForm').submit();
|
||||
</script>
|
||||
<?php
|
||||
|
||||
exit;
|
||||
|
||||
|
||||
+134
-399
@@ -21,11 +21,6 @@ if (!$template) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Verifica i mapping
|
||||
$stmt = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$hasMappings = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
// Debug del template
|
||||
error_log("Loaded template: " . print_r($template, true));
|
||||
?>
|
||||
@@ -38,24 +33,7 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.top-scrollbar {
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
width: 100%;
|
||||
height: 18px;
|
||||
margin-bottom: 8px;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
background: #f8f9fa;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.top-scrollbar-inner {
|
||||
height: 1px;
|
||||
}
|
||||
|
||||
.table-container {
|
||||
overflow-x: auto;
|
||||
max-width: 100%;
|
||||
@@ -137,16 +115,6 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
|
||||
.column-filters th {
|
||||
background: #ffffff;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.column-filters input {
|
||||
width: 100%;
|
||||
min-width: 80px;
|
||||
}
|
||||
</style>
|
||||
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
@@ -159,72 +127,56 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
<div class="page-content">
|
||||
<?php include('top_stat_widget.php'); ?>
|
||||
<div class="mb-3 text">
|
||||
<a href="imported.php?id=<?= $id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="tolims.php?id=<?= $id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=i" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=P" class="btn btn-primary me-2">In Progress (P)</a>
|
||||
<a href="historical_trf.php?id=<?= $id ?>&status=l" class="btn btn-success">To LIMS (l)</a>
|
||||
</div>
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?></h6>
|
||||
<small>
|
||||
Template ID: <?= $id ?>,
|
||||
Sheet Number: <?= (int)($template['xls_sheet_index'] ?? 0) ?>,
|
||||
Start Row: <?= $template['header_row'] ?>,
|
||||
Start Column: <?= $template['start_column'] ?>
|
||||
</small>
|
||||
<small>Template ID: <?= $id ?>, Start Row: <?= $template['header_row'] ?>, Start Column: <?= $template['start_column'] ?></small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<?php if (!$hasMappings): ?>
|
||||
<div class="alert alert-warning" role="alert">
|
||||
Nessun mapping trovato per questo template. Configura i mapping prima di procedere.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<!-- Form per caricare il file -->
|
||||
<form id="uploadForm" enctype="multipart/form-data" class="mb-4">
|
||||
<div class="mb-3">
|
||||
<label for="excel_file" class="form-label">Upload XLS File</label>
|
||||
<input type="file" class="form-control" id="excel_file" name="excel_file" accept=".xls,.xlsx" required>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" <?= !$hasMappings ? 'disabled' : '' ?>>Upload</button>
|
||||
<button type="submit" class="btn btn-primary">Upload</button>
|
||||
<div class="loader" id="loader"></div>
|
||||
</form>
|
||||
|
||||
<!-- Contenitore per messaggi di errore -->
|
||||
<div id="errorContainer" class="alert alert-danger mt-3" style="display: none;"></div>
|
||||
|
||||
<!-- Contenitore per la tabella -->
|
||||
<div id="tableContainer"></div>
|
||||
|
||||
<div class="modal fade" id="routineConfirmModal" tabindex="-1" aria-labelledby="routineConfirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="routineConfirmModalLabel">Conferma Applicazione Routine</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p><strong>Routine:</strong> <span id="routineName"></span></p>
|
||||
<p><strong>Descrizione:</strong> <span id="routineDescription"></span></p>
|
||||
<p>Vuoi applicare questa routine al file caricato?</p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal" id="cancelRoutineBtn">Annulla</button>
|
||||
<button type="button" class="btn btn-primary" id="confirmRoutineBtn">Applica</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--end page wrapper -->
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<!--end wrapper-->
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.11.6/dist/umd/popper.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.min.js"></script>
|
||||
<!-- search modal -->
|
||||
<?php //include('include/searchmodal.php');
|
||||
?>
|
||||
<!-- end search modal -->
|
||||
|
||||
<!--start switcher-->
|
||||
<?php //include('include/themeswitcher.php');
|
||||
?>
|
||||
<!--end switcher-->
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
@@ -232,12 +184,6 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
const loader = document.getElementById('loader');
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
const tableContainer = document.getElementById('tableContainer');
|
||||
const routineModal = new bootstrap.Modal(document.getElementById('routineConfirmModal'));
|
||||
const confirmRoutineBtn = document.getElementById('confirmRoutineBtn');
|
||||
const cancelRoutineBtn = document.getElementById('cancelRoutineBtn');
|
||||
let routineData = null;
|
||||
let excelData = null;
|
||||
let responseData = null;
|
||||
|
||||
form.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
@@ -249,351 +195,140 @@ error_log("Loaded template: " . print_r($template, true));
|
||||
const templateId = <?= $id ?>;
|
||||
console.log('Template ID passed to formData:', templateId);
|
||||
formData.append('template_id', templateId);
|
||||
formData.append('header_row', <?= (int)$template['header_row'] ?>);
|
||||
formData.append('start_column', <?= json_encode($template['start_column']) ?>);
|
||||
formData.append('xls_sheet_index', <?= (int)($template['xls_sheet_index'] ?? 0) ?>);
|
||||
formData.append('header_row', <?= $template['header_row'] ?>);
|
||||
formData.append('start_column', <?= $template['start_column'] ?>);
|
||||
|
||||
fetch('process_import_xls2.php', {
|
||||
method: 'POST',
|
||||
body: formData
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Stato risposta:', response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log('Risposta JSON:', data);
|
||||
loader.style.display = 'none';
|
||||
if (data.error) {
|
||||
errorContainer.textContent = data.error;
|
||||
errorContainer.style.display = 'block';
|
||||
} else if (data.apply_routine) {
|
||||
console.log('Routine rilevata:', data.routine_data);
|
||||
routineData = data.routine_data;
|
||||
excelData = data.excel_data;
|
||||
responseData = data;
|
||||
document.getElementById('routineName').textContent = routineData.name || 'Sconosciuta';
|
||||
document.getElementById('routineDescription').textContent = routineData.instruction || 'Nessuna descrizione';
|
||||
routineModal.show();
|
||||
} else {
|
||||
console.log('Nessuna routine, procedo con tabella');
|
||||
showTable(data);
|
||||
let html = `
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||
<input type="hidden" name="columns" value='${JSON.stringify(data.columns)}'>
|
||||
<input type="hidden" name="rows" value='${JSON.stringify(data.rows)}'>
|
||||
<input type="hidden" name="filename" value="${data.filename}">
|
||||
<div class="search-container">
|
||||
<input type="text" id="searchInput" class="form-control" placeholder="Cerca nelle righe...">
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
${data.columns.map(col => `<th>${col || 'Colonna senza nome'}<div class="resize-handle"></div></th>`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.rows.map((row, index) => `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}"></td>
|
||||
${row.map(cell => `<td>${cell}</td>`).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButton" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
tableContainer.innerHTML = html;
|
||||
|
||||
// Inizializza le variabili dopo aver inserito la tabella
|
||||
const proceedButton = document.getElementById('proceedButton');
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
|
||||
// Funzione per aggiornare lo stato del pulsante Prosegui
|
||||
function updateProceedButton() {
|
||||
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
|
||||
}
|
||||
|
||||
// Event listener per il checkbox "Seleziona tutto"
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.checked = this.checked;
|
||||
});
|
||||
updateProceedButton();
|
||||
});
|
||||
|
||||
// Event listener per i checkbox delle righe
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
console.log('Checkbox changed, checked: ', this.checked); // Debug
|
||||
// Aggiorna lo stato del checkbox "Seleziona tutto"
|
||||
selectAllCheckbox.checked = Array.from(checkboxes).every(cb => cb.checked);
|
||||
updateProceedButton();
|
||||
});
|
||||
});
|
||||
|
||||
// Aggiungi logica per il ridimensionamento delle colonne
|
||||
const thElements = document.querySelectorAll('.table th');
|
||||
thElements.forEach((th, index) => {
|
||||
if (index === 0) return;
|
||||
const resizeHandle = th.querySelector('.resize-handle');
|
||||
if (resizeHandle) {
|
||||
resizeHandle.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
const startX = e.clientX;
|
||||
const startWidth = th.offsetWidth;
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const newWidth = Math.max(50, startWidth + (e.clientX - startX));
|
||||
th.style.width = `${newWidth}px`;
|
||||
th.style.minWidth = `${newWidth}px`;
|
||||
th.style.maxWidth = `${newWidth}px`;
|
||||
|
||||
const cells = document.querySelectorAll(`.table td:nth-child(${index + 1})`);
|
||||
cells.forEach(cell => {
|
||||
cell.style.width = `${newWidth}px`;
|
||||
cell.style.minWidth = `${newWidth}px`;
|
||||
cell.style.maxWidth = `${newWidth}px`;
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Aggiungi event listener per la ricerca
|
||||
const searchInput = document.getElementById('searchInput');
|
||||
const rows = document.querySelectorAll('.table tbody tr');
|
||||
|
||||
searchInput.addEventListener('input', function() {
|
||||
const searchTerm = this.value.toLowerCase();
|
||||
rows.forEach(row => {
|
||||
const text = Array.from(row.cells).slice(1).map(cell => cell.textContent.toLowerCase()).join(' ');
|
||||
row.style.display = text.includes(searchTerm) ? '' : 'none';
|
||||
});
|
||||
});
|
||||
|
||||
// Abilita il pulsante se ci sono checkbox selezionate all'inizio
|
||||
updateProceedButton();
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Errore fetch:', error);
|
||||
loader.style.display = 'none';
|
||||
errorContainer.textContent = 'Errore durante il caricamento del file: ' + error.message;
|
||||
errorContainer.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
confirmRoutineBtn.addEventListener('click', function() {
|
||||
console.log('Conferma routine:', routineData);
|
||||
routineModal.hide();
|
||||
|
||||
fetch('apply_routine.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: <?= $id ?>,
|
||||
filename: routineData.filename,
|
||||
headerrow: routineData.headerrow,
|
||||
excel_data: excelData,
|
||||
routine_data: routineData
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
console.log('Stato apply_routine:', response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(data => {
|
||||
console.log('Risposta apply_routine:', data);
|
||||
if (data.error) {
|
||||
errorContainer.textContent = data.error;
|
||||
errorContainer.style.display = 'block';
|
||||
} else {
|
||||
showTable(data);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.log('Errore apply_routine:', error);
|
||||
errorContainer.textContent = 'Errore durante l\'applicazione della routine: ' + error.message;
|
||||
errorContainer.style.display = 'block';
|
||||
});
|
||||
});
|
||||
|
||||
cancelRoutineBtn.addEventListener('click', function() {
|
||||
console.log('Routine annullata, procedo con tabella');
|
||||
routineModal.hide();
|
||||
showTable(responseData);
|
||||
});
|
||||
|
||||
function showTable(data) {
|
||||
console.log('Mostro tabella con dati:', data);
|
||||
let html = `
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${data.template_id}">
|
||||
<input type="hidden" name="columns" value="${encodeURIComponent(JSON.stringify(data.columns))}">
|
||||
<input type="hidden" name="rows" id="selectedRowsData" value="">
|
||||
<input type="hidden" name="excelrows" id="selectedExcelRowsData" value="">
|
||||
<input type="hidden" name="filename" value="${data.filename}">
|
||||
|
||||
<!-- TOP BUTTON -->
|
||||
<div class="d-flex justify-content-end mb-3">
|
||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||
</div>
|
||||
|
||||
<div class="top-scrollbar" id="topTableScrollbar">
|
||||
<div class="top-scrollbar-inner" id="topTableScrollbarInner"></div>
|
||||
</div>
|
||||
|
||||
<div class="table-container" id="mainTableContainer">
|
||||
<table class="table table-striped table-bordered" id="importPreviewTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><input type="checkbox" id="selectAll"> Seleziona</th>
|
||||
${data.columns.map(col => {
|
||||
const label = !col ? 'Colonna senza nome' : (col.match(/^__empty_\d+__$/) ? 'Colonna senza nome' : col);
|
||||
return `<th>${label}<div class="resize-handle"></div></th>`;
|
||||
}).join('')}
|
||||
</tr>
|
||||
<tr class="column-filters">
|
||||
<th></th>
|
||||
${data.columns.map((col, i) => `
|
||||
<th>
|
||||
<input type="text"
|
||||
class="form-control form-control-sm column-filter"
|
||||
data-col-index="${i}"
|
||||
placeholder="Filter...">
|
||||
</th>
|
||||
`).join('')}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr>
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${row.excelrow}"></td>
|
||||
${row.data.map(cell => `<td>${cell}</td>`).join('')}
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- BOTTOM BUTTON -->
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButtonBottom" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
tableContainer.innerHTML = html;
|
||||
|
||||
const selectRowsForm = document.getElementById('selectRowsForm');
|
||||
|
||||
selectRowsForm.addEventListener('submit', function(e) {
|
||||
const checkedBoxes = Array.from(document.querySelectorAll('.row-checkbox:checked'));
|
||||
|
||||
if (checkedBoxes.length === 0) {
|
||||
e.preventDefault();
|
||||
alert('Seleziona almeno una riga.');
|
||||
return;
|
||||
}
|
||||
|
||||
const selectedRows = [];
|
||||
const selectedExcelRows = [];
|
||||
|
||||
checkedBoxes.forEach((cb, newIndex) => {
|
||||
const originalIndex = parseInt(cb.value, 10);
|
||||
|
||||
if (data.rows && data.rows[originalIndex]) {
|
||||
selectedRows.push(data.rows[originalIndex]);
|
||||
}
|
||||
|
||||
if (data.excel_data && data.excel_data[originalIndex]) {
|
||||
selectedExcelRows.push(data.excel_data[originalIndex].excelrow);
|
||||
}
|
||||
|
||||
// Reindex selected_rows so import_insert.php receives only the reduced rows array
|
||||
cb.value = newIndex;
|
||||
});
|
||||
|
||||
document.getElementById('selectedRowsData').value =
|
||||
encodeURIComponent(JSON.stringify(selectedRows));
|
||||
|
||||
document.getElementById('selectedExcelRowsData').value =
|
||||
encodeURIComponent(JSON.stringify(selectedExcelRows));
|
||||
});
|
||||
|
||||
const topTableScrollbar = document.getElementById('topTableScrollbar');
|
||||
const topTableScrollbarInner = document.getElementById('topTableScrollbarInner');
|
||||
const mainTableContainer = document.getElementById('mainTableContainer');
|
||||
const importPreviewTable = document.getElementById('importPreviewTable');
|
||||
|
||||
function updateTopTableScrollbar() {
|
||||
if (!topTableScrollbar || !topTableScrollbarInner || !mainTableContainer || !importPreviewTable) return;
|
||||
|
||||
topTableScrollbarInner.style.width = importPreviewTable.scrollWidth + 'px';
|
||||
|
||||
if (mainTableContainer.scrollWidth > mainTableContainer.clientWidth) {
|
||||
topTableScrollbar.style.display = 'block';
|
||||
} else {
|
||||
topTableScrollbar.style.display = 'none';
|
||||
}
|
||||
}
|
||||
|
||||
let syncingTop = false;
|
||||
let syncingBottom = false;
|
||||
|
||||
if (topTableScrollbar && mainTableContainer) {
|
||||
topTableScrollbar.addEventListener('scroll', function() {
|
||||
if (syncingBottom) return;
|
||||
syncingTop = true;
|
||||
mainTableContainer.scrollLeft = topTableScrollbar.scrollLeft;
|
||||
syncingTop = false;
|
||||
});
|
||||
|
||||
mainTableContainer.addEventListener('scroll', function() {
|
||||
if (syncingTop) return;
|
||||
syncingBottom = true;
|
||||
topTableScrollbar.scrollLeft = mainTableContainer.scrollLeft;
|
||||
syncingBottom = false;
|
||||
});
|
||||
}
|
||||
|
||||
updateTopTableScrollbar();
|
||||
setTimeout(updateTopTableScrollbar, 100);
|
||||
setTimeout(updateTopTableScrollbar, 300);
|
||||
window.addEventListener('resize', updateTopTableScrollbar);
|
||||
|
||||
const proceedButtonTop = document.getElementById('proceedButtonTop');
|
||||
const proceedButtonBottom = document.getElementById('proceedButtonBottom');
|
||||
const selectAllCheckbox = document.getElementById('selectAll');
|
||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
||||
|
||||
function updateProceedButton() {
|
||||
const enabled = Array.from(checkboxes).some(cb => cb.checked);
|
||||
|
||||
if (proceedButtonTop) proceedButtonTop.disabled = !enabled;
|
||||
if (proceedButtonBottom) proceedButtonBottom.disabled = !enabled;
|
||||
}
|
||||
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
const visibleRows = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||
.filter(row => row.style.display !== 'none');
|
||||
|
||||
visibleRows.forEach(row => {
|
||||
const checkbox = row.querySelector('.row-checkbox');
|
||||
if (checkbox) {
|
||||
checkbox.checked = this.checked;
|
||||
}
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
});
|
||||
|
||||
checkboxes.forEach(checkbox => {
|
||||
checkbox.addEventListener('change', function() {
|
||||
console.log('Checkbox changed, checked:', this.checked, 'excelrow:', this.dataset.excelrow);
|
||||
|
||||
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||
.filter(row => row.style.display !== 'none')
|
||||
.map(row => row.querySelector('.row-checkbox'))
|
||||
.filter(cb => cb !== null);
|
||||
|
||||
selectAllCheckbox.checked =
|
||||
visibleCheckboxes.length > 0 &&
|
||||
visibleCheckboxes.every(cb => cb.checked);
|
||||
|
||||
updateProceedButton();
|
||||
});
|
||||
});
|
||||
|
||||
const thElements = document.querySelectorAll('.table th');
|
||||
thElements.forEach((th, index) => {
|
||||
if (index === 0) return;
|
||||
const resizeHandle = th.querySelector('.resize-handle');
|
||||
if (resizeHandle) {
|
||||
resizeHandle.addEventListener('mousedown', (e) => {
|
||||
e.preventDefault();
|
||||
const startX = e.clientX;
|
||||
const startWidth = th.offsetWidth;
|
||||
|
||||
const onMouseMove = (e) => {
|
||||
const newWidth = Math.max(50, startWidth + (e.clientX - startX));
|
||||
th.style.width = `${newWidth}px`;
|
||||
th.style.minWidth = `${newWidth}px`;
|
||||
th.style.maxWidth = `${newWidth}px`;
|
||||
|
||||
const cells = document.querySelectorAll(`.table td:nth-child(${index + 1})`);
|
||||
cells.forEach(cell => {
|
||||
cell.style.width = `${newWidth}px`;
|
||||
cell.style.minWidth = `${newWidth}px`;
|
||||
cell.style.maxWidth = `${newWidth}px`;
|
||||
});
|
||||
};
|
||||
|
||||
const onMouseUp = () => {
|
||||
document.removeEventListener('mousemove', onMouseMove);
|
||||
document.removeEventListener('mouseup', onMouseUp);
|
||||
};
|
||||
|
||||
document.addEventListener('mousemove', onMouseMove);
|
||||
document.addEventListener('mouseup', onMouseUp);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const rows = document.querySelectorAll('.table tbody tr');
|
||||
const filterInputs = document.querySelectorAll('.column-filter');
|
||||
|
||||
// Stato filtri: key = colIndex, value = testo
|
||||
const activeFilters = {};
|
||||
|
||||
function applyColumnFilters() {
|
||||
rows.forEach(row => {
|
||||
let visible = true;
|
||||
|
||||
for (const [colIndexStr, filterValue] of Object.entries(activeFilters)) {
|
||||
const colIndex = parseInt(colIndexStr, 10);
|
||||
const cell = row.cells[colIndex + 1];
|
||||
|
||||
const cellText = (cell?.textContent || '').toLowerCase();
|
||||
const searchText = (filterValue || '').toLowerCase().trim();
|
||||
|
||||
if (searchText && !cellText.includes(searchText)) {
|
||||
visible = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
row.style.display = visible ? '' : 'none';
|
||||
});
|
||||
|
||||
const visibleCheckboxes = Array.from(document.querySelectorAll('.table tbody tr'))
|
||||
.filter(row => row.style.display !== 'none')
|
||||
.map(row => row.querySelector('.row-checkbox'))
|
||||
.filter(cb => cb !== null);
|
||||
|
||||
selectAllCheckbox.checked =
|
||||
visibleCheckboxes.length > 0 &&
|
||||
visibleCheckboxes.every(cb => cb.checked);
|
||||
|
||||
updateProceedButton();
|
||||
}
|
||||
|
||||
filterInputs.forEach(input => {
|
||||
input.addEventListener('input', function() {
|
||||
const colIndex = this.dataset.colIndex; // string
|
||||
activeFilters[colIndex] = this.value;
|
||||
applyColumnFilters();
|
||||
});
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
@@ -1,213 +0,0 @@
|
||||
<?php
|
||||
|
||||
if (!function_exists('gdbFixedDefaultValue')) {
|
||||
/** Default di un fixed field (DATE 'today' → data odierna). */
|
||||
function gdbFixedDefaultValue(array $f): string
|
||||
{
|
||||
$v = $f['default_value'] ?? '';
|
||||
if (($f['data_type'] ?? '') === 'DATE' && $v === 'today') {
|
||||
return date('Y-m-d');
|
||||
}
|
||||
return (string)$v;
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('buildGridConfig')) {
|
||||
/**
|
||||
* Config del template necessaria per costruire le righe: fixed fields (ordinati
|
||||
* come in imported.php), main field mappings, alias map, default idclient.
|
||||
*
|
||||
* @return array{fixedFields:array, fixedAliasMap:array, mainFieldMappings:array, default_idclient:mixed}
|
||||
*/
|
||||
function buildGridConfig(PDO $pdo, int $templateId): array
|
||||
{
|
||||
// Mappa logica fixed_field_key → colonna reale su datadb (come imported.php)
|
||||
$fixedAliasMap = [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'ClienteFatturazione' => 'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||
];
|
||||
|
||||
// Fixed fields visibili, ordinati come imported.php
|
||||
$fixedStmt = $pdo->prepare("
|
||||
SELECT id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ? AND is_visible_import = 1
|
||||
ORDER BY id
|
||||
");
|
||||
$fixedStmt->execute([$templateId]);
|
||||
$fixedFieldsRaw = $fixedStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$desiredOrder = [
|
||||
'ClienteResponsabile', 'ClienteFornitore', 'ClienteAnalisi', 'ClienteFatturazione',
|
||||
'AnagraficaCertestObject', 'AnagraficaCertestService', 'MoltiplicatorePrezzo', 'ConsegnaRichiesta',
|
||||
];
|
||||
$excludeFromFixed = ['ClienteFornitore']; // reso come colonna a sé
|
||||
|
||||
$fixedFields = [];
|
||||
$tempMap = [];
|
||||
foreach ($fixedFieldsRaw as $f) {
|
||||
if (in_array($f['fixed_field_key'], $excludeFromFixed, true)) continue;
|
||||
$tempMap[$f['fixed_field_key']] = $f;
|
||||
}
|
||||
foreach ($desiredOrder as $key) {
|
||||
if (isset($tempMap[$key])) {
|
||||
$fixedFields[] = $tempMap[$key];
|
||||
unset($tempMap[$key]);
|
||||
}
|
||||
}
|
||||
foreach ($tempMap as $f) {
|
||||
$fixedFields[] = $f;
|
||||
}
|
||||
|
||||
// Main field mappings (max 2), come imported.php
|
||||
$mapStmt = $pdo->prepare("
|
||||
SELECT id, field_label, data_type, is_required, field_id, field_order,
|
||||
main_field, is_visible_import, manual_default
|
||||
FROM template_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY field_order ASC, id ASC
|
||||
");
|
||||
$mapStmt->execute([$templateId]);
|
||||
$allMappings = $mapStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$mainFieldMappings = [];
|
||||
foreach ($allMappings as $mapping) {
|
||||
if ((string)$mapping['main_field'] === '1' && (int)$mapping['is_visible_import'] === 1) {
|
||||
$mainFieldMappings[] = $mapping;
|
||||
}
|
||||
if (count($mainFieldMappings) >= 2) break;
|
||||
}
|
||||
|
||||
// Default idclient dal template
|
||||
$tplStmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
|
||||
$tplStmt->execute([$templateId]);
|
||||
$default_idclient = $tplStmt->fetchColumn();
|
||||
$default_idclient = $default_idclient !== false ? $default_idclient : null;
|
||||
|
||||
return [
|
||||
'fixedFields' => $fixedFields,
|
||||
'fixedAliasMap' => $fixedAliasMap,
|
||||
'mainFieldMappings' => $mainFieldMappings,
|
||||
'default_idclient' => $default_idclient,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('buildGridRows')) {
|
||||
/**
|
||||
* Costruisce le righe gridData per la lista di iddatadb data, nell'ordine passato.
|
||||
*
|
||||
* @param int[] $iddatadbList
|
||||
* @param array $config output di buildGridConfig()
|
||||
* @return array righe nella stessa forma di imported.php $gridDataArray
|
||||
*/
|
||||
function buildGridRows(PDO $pdo, array $iddatadbList, array $config): array
|
||||
{
|
||||
$iddatadbList = array_values(array_filter(array_map('intval', $iddatadbList), fn($v) => $v > 0));
|
||||
if (empty($iddatadbList)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$fixedFields = $config['fixedFields'];
|
||||
$fixedAliasMap = $config['fixedAliasMap'];
|
||||
$mainFieldMappings = $config['mainFieldMappings'];
|
||||
$default_idclient = $config['default_idclient'];
|
||||
|
||||
$ph = implode(',', array_fill(0, count($iddatadbList), '?'));
|
||||
|
||||
// Righe datadb + user_name
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT d.*, CONCAT(u.first_name, ' ', u.last_name) AS user_name
|
||||
FROM datadb d
|
||||
LEFT JOIN auth_users u ON d.user_id = u.id
|
||||
WHERE d.iddatadb IN ($ph)
|
||||
");
|
||||
$stmt->execute($iddatadbList);
|
||||
$byId = [];
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||
$byId[(int)$r['iddatadb']] = $r;
|
||||
}
|
||||
|
||||
// Dettagli (import_data_details)
|
||||
$detStmt = $pdo->prepare("
|
||||
SELECT d.id AS datadb_id, d.mapping_id, d.field_value,
|
||||
m.field_id, m.field_label, m.data_type, m.is_required, m.manual_default
|
||||
FROM import_data_details d
|
||||
JOIN template_mapping m ON d.mapping_id = m.id
|
||||
WHERE d.id IN ($ph)
|
||||
");
|
||||
$detStmt->execute($iddatadbList);
|
||||
$detailsByRow = [];
|
||||
foreach ($detStmt->fetchAll(PDO::FETCH_ASSOC) as $d) {
|
||||
$detailsByRow[(int)$d['datadb_id']][] = $d;
|
||||
}
|
||||
|
||||
// Costruzione righe nell'ordine della lista passata
|
||||
$rows = [];
|
||||
foreach ($iddatadbList as $id) {
|
||||
$row = $byId[$id] ?? null;
|
||||
if ($row === null) continue;
|
||||
|
||||
$rowObj = [
|
||||
'iddatadb' => (int)$row['iddatadb'],
|
||||
'status' => $row['status'] ?? 'i',
|
||||
'idclient' => $row['idclient'] ?? $default_idclient,
|
||||
'cliente_fornitore_id' => $row['cliente_fornitore_id'] ?? null,
|
||||
'tested_component' => $row['tested_component'] ?? '',
|
||||
'commessaweb' => $row['commessaweb'] ?? null,
|
||||
'user_name' => $row['user_name'] ?? '',
|
||||
'importreferencecode' => $row['importreferencecode'] ?? '',
|
||||
'filename_import' => $row['filename_import'] ?? '',
|
||||
'importdate' => $row['importdate'] ?? '',
|
||||
];
|
||||
|
||||
// Fixed fields
|
||||
$rowObj['fixedFields'] = [];
|
||||
foreach ($fixedFields as $f) {
|
||||
$key = $f['fixed_field_key'];
|
||||
$dbCol = $fixedAliasMap[$key] ?? $key;
|
||||
$val = $row[$dbCol] ?? '';
|
||||
if ($val === '' || $val === null) {
|
||||
$val = gdbFixedDefaultValue($f);
|
||||
}
|
||||
$rowObj['fixedFields'][$key] = (string)$val;
|
||||
}
|
||||
|
||||
// Details
|
||||
$rowObj['details'] = [];
|
||||
$rowDetails = $detailsByRow[$id] ?? [];
|
||||
foreach ($rowDetails as $d) {
|
||||
$rowObj['details'][(string)$d['mapping_id']] = $d['field_value'] ?? '';
|
||||
}
|
||||
|
||||
// Main field values
|
||||
foreach ($mainFieldMappings as $mainMapping) {
|
||||
$found = null;
|
||||
foreach ($rowDetails as $d) {
|
||||
if ($d['mapping_id'] == $mainMapping['id']) {
|
||||
$found = $d;
|
||||
break;
|
||||
}
|
||||
}
|
||||
$rowObj['details'][(string)$mainMapping['id']] =
|
||||
($found['field_value'] ?? null) ?? ($mainMapping['manual_default'] ?? '');
|
||||
}
|
||||
|
||||
if (!empty($mainFieldMappings)) {
|
||||
$firstMain = $mainFieldMappings[0];
|
||||
$rowObj['mainFieldValue'] = $rowObj['details'][(string)$firstMain['id']] ?? '';
|
||||
}
|
||||
|
||||
$rowObj['_dirty'] = false;
|
||||
$rows[] = $rowObj;
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
}
|
||||
@@ -5,14 +5,11 @@ $db = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
|
||||
include('../../extra/auth.php');
|
||||
//require_once __DIR__ . '/extra/auth.php';
|
||||
|
||||
// Laravel bootstrap (loaded by auth.php) forces UTC via config/app.php — re-apply our TZ
|
||||
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
|
||||
|
||||
// Here we just check if user is not
|
||||
// logged in, and in that case we redirect
|
||||
// the user to vanguard login page.
|
||||
@@ -29,8 +26,6 @@ $nameuser = $user->present()->first_name;
|
||||
$surnameuser = $user->present()->last_name;
|
||||
$emailuser = $user->present()->email;
|
||||
$avatar = $user->present()->avatar;
|
||||
$lims_user_id = $user->lims_user_id ?? '';
|
||||
$lims_global_user_id = $user->lims_global_user_id ?? '';
|
||||
|
||||
$kindofrole = $user->present()->role_id;
|
||||
|
||||
@@ -40,6 +35,8 @@ $kindofrole = $user->present()->role_id;
|
||||
//$iduserlogin="1";
|
||||
//$nameuser="Claudio";
|
||||
//$emailuser="info@claudiosironi.com";
|
||||
?>
|
||||
<?php
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
@@ -52,8 +49,13 @@ $_SESSION["emailuser"] = $emailuser;
|
||||
$_SESSION["photouser"] = $avatar;
|
||||
$photouser = $_SESSION["photouser"];
|
||||
$photousername = basename($avatar);
|
||||
?>
|
||||
|
||||
|
||||
<?php //include files
|
||||
|
||||
//include files
|
||||
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||
|
||||
//include("generalsettings.php");
|
||||
//include("generalsettings.php");
|
||||
|
||||
?>
|
||||
@@ -1,25 +0,0 @@
|
||||
<?php
|
||||
require_once(__DIR__ . '/../class/db-functions.php');
|
||||
|
||||
$db = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
|
||||
// Inizializza la sessione
|
||||
if (session_status() == PHP_SESSION_NONE) {
|
||||
session_start();
|
||||
}
|
||||
|
||||
// Imposta variabili di sessione di default per evitare errori
|
||||
$_SESSION['iduserlogin'] = '1'; // Nessun utente loggato
|
||||
$_SESSION['nameuser'] = 'Ospite';
|
||||
$_SESSION['surnameuser'] = '';
|
||||
$_SESSION['emailuser'] = '';
|
||||
$_SESSION['photouser'] = '';
|
||||
$photouser = $_SESSION['photouser'];
|
||||
$photousername = '';
|
||||
$iduserlogin = $_SESSION['iduserlogin'];
|
||||
// Include file di lingua, se necessario
|
||||
require_once(__DIR__ . '/../../languages/en/general.php');
|
||||
@@ -6,163 +6,88 @@
|
||||
<div>
|
||||
<h4 class="logo-text"><?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></h4>
|
||||
</div>
|
||||
<div class="toggle-icon ms-auto">
|
||||
<i class='bx bx-arrow-back'></i>
|
||||
<div class="toggle-icon ms-auto"><i class='bx bx-arrow-back'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--navigation-->
|
||||
<ul class="metismenu" id="menu">
|
||||
|
||||
<?php
|
||||
$currentUser = Auth::user();
|
||||
|
||||
$isAdmin = $currentUser->hasRole('Admin');
|
||||
$isSuperUser = $currentUser->hasRole('SuperUser');
|
||||
$isUser = $currentUser->hasRole('User');
|
||||
|
||||
$canAccessMainMenu = $isAdmin || $isSuperUser || $isUser;
|
||||
$canAccessUserAdmin = $isAdmin || $isSuperUser;
|
||||
$canAccessTemplates = $isAdmin || $isSuperUser;
|
||||
?>
|
||||
|
||||
<!-- user, admin, superuser menu -->
|
||||
<?php if ($canAccessMainMenu) : ?>
|
||||
|
||||
<!-- user, admin, superuser menù -->
|
||||
<?php if ((Auth::user()->hasRole('Admin')) || (Auth::user()->hasRole('User')) || (Auth::user()->hasRole('Superuser'))) : ?>
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon">
|
||||
<i class='bx bx-home-alt'></i>
|
||||
<div class="parent-icon"><i class='bx bx-home-alt'></i>
|
||||
</div>
|
||||
<div class="menu-title">Dashboard</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="import_dashboard.php">
|
||||
<i class='bx bx-radio-circle'></i>XLS Import
|
||||
</a>
|
||||
<!-- <li> <a href="index.php"><i class='bx bx-radio-circle'></i>Default</a>
|
||||
</li> -->
|
||||
<li> <a href="import_dashboard.php"><i class='bx bx-radio-circle'></i>XLS Import</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<?php if ($canAccessTemplates) : ?>
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-category"></i>
|
||||
</div>
|
||||
<div class="menu-title">Templates</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="templates_dashboard.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($dashtemplate, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="insert_template_xls.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($insertnewtemplatexls, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-category"></i>
|
||||
<div class="parent-icon"><i class="bx bx-category"></i>
|
||||
</div>
|
||||
<div class="menu-title">Other Functions</div>
|
||||
<div class="menu-title">Templates</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="quotations.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($quotationstitle, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
<li> <a href="templates_dashboard.php"><i class='bx bx-radio-circle'></i><?= htmlspecialchars($dashtemplate, ENT_QUOTES, 'UTF-8'); ?></a>
|
||||
</li>
|
||||
<li> <a href="insert_template_xls.php"><i class='bx bx-radio-circle'></i><?= htmlspecialchars($insertnewtemplatexls, ENT_QUOTES, 'UTF-8'); ?></a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
</li>-->
|
||||
|
||||
|
||||
<?php if ($canAccessUserAdmin) : ?>
|
||||
<li class="menu-label">Administration</li>
|
||||
|
||||
<li>
|
||||
<a href="user-admin.php">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-user-plus"></i>
|
||||
</div>
|
||||
<div class="menu-title">User Admin</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="stats_export_lims.php">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-bar-chart-alt-2"></i>
|
||||
</div>
|
||||
<div class="menu-title">Stats</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="warm_cache_run.php">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-bar-chart-alt-2"></i>
|
||||
</div>
|
||||
<div class="menu-title">Warm Cache</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<li class="menu-label">Others</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="https://helpdesk.cesoft.io" target="_blank">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-support"></i>
|
||||
<div class="parent-icon"><i class="bx bx-support"></i>
|
||||
</div>
|
||||
<div class="menu-title">Support</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<!-- admin only menu -->
|
||||
<?php if ($isAdmin) : ?>
|
||||
|
||||
<?php
|
||||
endif; ?>
|
||||
<!-- admin, superuser menù -->
|
||||
<?php if ((Auth::user()->hasRole('Admin')) || (Auth::user()->hasRole('Superuser'))) : ?>
|
||||
<?php
|
||||
endif; ?>
|
||||
<!-- admin menù -->
|
||||
<?php if (Auth::user()->hasRole('Admin')) : ?>
|
||||
<li class="menu-label">Admin Menù</li>
|
||||
|
||||
<li>
|
||||
<a href="../" target="_blank">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-support"></i>
|
||||
<div class="parent-icon"><i class="bx bx-support"></i>
|
||||
</div>
|
||||
<div class="menu-title">User Management</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<!--
|
||||
<li>
|
||||
<!-- <li>
|
||||
<a href="template/index.html" target="_blank">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-support"></i>
|
||||
<div class="parent-icon"><i class="bx bx-support"></i>
|
||||
</div>
|
||||
<div class="menu-title">Template</div>
|
||||
<div class="menu-title">Template</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://codervent.com/rocker/documentation/index.html" target="_blank">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-folder"></i>
|
||||
<div class="parent-icon"><i class="bx bx-folder"></i>
|
||||
</div>
|
||||
<div class="menu-title">Documentation</div>
|
||||
</a>
|
||||
</li>
|
||||
-->
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</li> -->
|
||||
<?php
|
||||
endif; ?>
|
||||
</ul>
|
||||
<!--end navigation-->
|
||||
</div>
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
$popupEnabled = (($_ENV['POPUP_ENABLED'] ?? getenv('POPUP_ENABLED')) === 'true');
|
||||
|
||||
if ($popupEnabled):
|
||||
?>
|
||||
<div id="app-popup-overlay" style="position:fixed;inset:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;z-index:99999;padding:20px;">
|
||||
<div style="background:#fff;max-width:640px;width:100%;max-height:90vh;overflow-y:auto;padding:40px;border-radius:14px;position:relative;box-shadow:0 20px 60px rgba(0,0,0,.4);text-align:center;">
|
||||
|
||||
<div style="font-size:46px;line-height:1;margin-bottom:10px;">🚀</div>
|
||||
|
||||
<h2 style="margin:0 0 8px;font-size:26px;color:#111;">Nuovo sito in produzione</h2>
|
||||
<p style="font-size:16px;color:#444;margin:0 0 20px;">Il portale si è spostato su un nuovo indirizzo.</p>
|
||||
|
||||
<div style="background:#f1f5ff;border:1px solid #c9d8ff;border-radius:10px;padding:16px;margin-bottom:24px;">
|
||||
<p style="margin:0 0 4px;font-size:13px;color:#667;text-transform:uppercase;letter-spacing:.5px;">Nuovo indirizzo</p>
|
||||
<p style="margin:0;font-size:19px;font-weight:700;color:#1d4ed8;word-break:break-all;">https://trfsmart-bv.cesoft.io/</p>
|
||||
</div>
|
||||
|
||||
<a href="https://trfsmart-bv.cesoft.io/"
|
||||
style="display:inline-block;padding:16px 36px;background:#2563eb;color:#fff;text-decoration:none;border-radius:8px;font-size:18px;font-weight:600;box-shadow:0 4px 14px rgba(37,99,235,.35);">
|
||||
Clicca qui per accedere al nuovo sito
|
||||
</a>
|
||||
|
||||
<p style="font-size:13px;color:#888;margin:16px 0 0;">Ti consigliamo di salvare il nuovo indirizzo nei preferiti.</p>
|
||||
|
||||
<hr style="border:none;border-top:1px solid #eee;margin:28px 0 20px;">
|
||||
|
||||
<div style="background:#fff8e6;border:1px solid #ffe08a;border-radius:10px;padding:16px 18px;text-align:left;margin-bottom:20px;">
|
||||
<p style="margin:0;font-size:14px;color:#8a6d00;">
|
||||
<strong>⚠️ Attenzione:</strong> chiudi questo messaggio <u>solo</u> se riscontri problemi
|
||||
con il nuovo sito. Continuando su questo portale userai la versione precedente.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button onclick="chiudiPopupMigrazione()"
|
||||
style="padding:11px 22px;background:#f0f0f0;color:#555;border:1px solid #ddd;border-radius:8px;cursor:pointer;font-size:14px;">
|
||||
Ho problemi col nuovo sito, resto su questo portale
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function chiudiPopupMigrazione() {
|
||||
var conferma = confirm(
|
||||
"Sei sicuro di voler restare sul portale precedente?\n\n" +
|
||||
"Il nuovo sito è quello ufficiale in produzione. " +
|
||||
"Chiudi questo avviso solo se hai riscontrato un problema reale sul nuovo sito."
|
||||
);
|
||||
if (conferma) {
|
||||
document.getElementById('app-popup-overlay').remove();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
@@ -1,22 +1,8 @@
|
||||
<header>
|
||||
<div class="topbar d-flex align-items-center">
|
||||
|
||||
<?php
|
||||
// Controllo ambiente TEST
|
||||
$testEnv = strtolower($_ENV['TEST_ENVIRONMENT'] ?? getenv('TEST_ENVIRONMENT') ?? 'true');
|
||||
|
||||
if ($testEnv === 'true'):
|
||||
?>
|
||||
<div class="alert alert-danger border-0 rounded-0 mb-0 py-2 text-center fw-bold fs-5 w-100 shadow-sm"
|
||||
style="margin: 0 -15px 0 -15px;">
|
||||
<i class='bx bx-test-tube me-2'></i>
|
||||
AMBIENTE DI TEST — NON È PRODUZIONE
|
||||
<nav class="navbar navbar-expand gap-3">
|
||||
<div class="mobile-toggle-menu"><i class='bx bx-menu'></i>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<nav class="navbar navbar-expand gap-3 w-100">
|
||||
<!-- Tutto il resto del tuo codice della topbar rimane identico -->
|
||||
<div class="mobile-toggle-menu"><i class='bx bx-menu'></i></div>
|
||||
|
||||
<div class="search-bar d-lg-block d-none" data-bs-toggle="modal" data-bs-target="#SearchModal">
|
||||
<!-- <a href="avascript:;" class="btn d-flex align-items-center"><i class='bx bx-search'></i>Search</a> -->
|
||||
@@ -108,14 +94,8 @@
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item d-flex align-items-center" href="user-profile.php"><i class="bx bx-user fs-5"></i><span>Profile</span></a>
|
||||
</li>
|
||||
<?php if ($user->hasRole('Admin') || $user->hasRole('SuperUser')): ?>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex align-items-center" href="user-admin.php">
|
||||
<i class="bx bx-user-plus fs-5"></i>
|
||||
<span>User Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li><a class="dropdown-item d-flex align-items-center" href="settings.php"><i class="bx bx-cog fs-5"></i><span>Settings</span></a>
|
||||
</li>
|
||||
<li>
|
||||
<div class="dropdown-divider mb-0"></div>
|
||||
</li>
|
||||
|
||||
@@ -1,58 +1,41 @@
|
||||
<?php include('include/headscript.php');
|
||||
|
||||
// Retrieve all routines from database
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$stmt = $pdo->prepare("SELECT * FROM routine");
|
||||
$stmt->execute();
|
||||
$routines = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Retrieve active API/JSON configurations
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, name, provider_code, api_type, php_class_name
|
||||
FROM api_configurations
|
||||
WHERE is_active = 1
|
||||
ORDER BY name ASC
|
||||
");
|
||||
$stmt->execute();
|
||||
$apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
?>
|
||||
<?php include('include/headscript.php'); ?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!--favicon-->
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<title>Insert Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
<title>Insert XLS Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--wrapper-->
|
||||
<div class="wrapper">
|
||||
<!--sidebar wrapper -->
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<!--end sidebar wrapper -->
|
||||
<!--start header -->
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<!--end header -->
|
||||
<!--start page wrapper -->
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5 class="mb-0">Insert New Template</h5>
|
||||
<h5 class="mb-0">Insert new XLS Template</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p class="mb-2">Fill the following form in order to create a new import template</p>
|
||||
<p class="mb-2">Fill the following form in order to create a new import XLS template</p>
|
||||
<p class="mb-2">Mandatory Fields</p>
|
||||
<ul class="mb-0">
|
||||
<li>Template Name</li>
|
||||
<li>Source Type</li>
|
||||
<li>Schema and Client</li>
|
||||
<li>Row Header, Column Header and Sheet Number only for XLS templates</li>
|
||||
<li>API / JSON Configuration only for API / JSON templates</li>
|
||||
<li>Row Header and Column Header: where the title of the excel starts</li>
|
||||
<li>Scheme and client</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
@@ -65,86 +48,22 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="col-12">
|
||||
<form id="insertTemplateForm" method="POST">
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||
<input type="text" name="name" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Source Type *</label>
|
||||
<select name="source_type" id="sourceType" class="form-control" required>
|
||||
<option value="XLS" selected>XLS</option>
|
||||
<option value="API">API / JSON</option>
|
||||
<option value="PDF">PDF</option>
|
||||
</select>
|
||||
<small class="text-muted">Choose the source used by this template</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="headerRowWrapper">
|
||||
<label class="form-label"><?= htmlspecialchars($rowheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||
<input type="number" name="header_row" id="headerRow" class="form-control" value="1" required>
|
||||
<input type="number" name="header_row" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="startColumnWrapper">
|
||||
<div class="mb-3">
|
||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
||||
<input type="text" name="start_column" id="startColumn" class="form-control" value="A" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="xlsSheetNumberWrapper">
|
||||
<label class="form-label">XLS Sheet Number</label>
|
||||
<input
|
||||
type="number"
|
||||
name="xls_sheet_index"
|
||||
id="xlsSheetIndex"
|
||||
class="form-control"
|
||||
min="0"
|
||||
value="0">
|
||||
<small class="text-muted">
|
||||
Use 0 for the first sheet, 1 for the second sheet, 2 for the third sheet, and so on.
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<div class="mb-3" id="apiConfigWrapper" style="display: none;">
|
||||
<label class="form-label">API / JSON Configuration *</label>
|
||||
<select name="api_config_id" id="apiConfigSelect" class="form-control">
|
||||
<option value="">Select an API configuration...</option>
|
||||
|
||||
<?php foreach ($apiConfigurations as $apiConfig): ?>
|
||||
<?php
|
||||
$apiLabelParts = [];
|
||||
|
||||
if (!empty($apiConfig['name'])) {
|
||||
$apiLabelParts[] = $apiConfig['name'];
|
||||
}
|
||||
|
||||
if (!empty($apiConfig['provider_code'])) {
|
||||
$apiLabelParts[] = '[' . $apiConfig['provider_code'] . ']';
|
||||
}
|
||||
|
||||
if (!empty($apiConfig['api_type'])) {
|
||||
$apiLabelParts[] = '(' . $apiConfig['api_type'] . ')';
|
||||
}
|
||||
|
||||
if (!empty($apiConfig['php_class_name'])) {
|
||||
$apiLabelParts[] = '- ' . $apiConfig['php_class_name'];
|
||||
}
|
||||
|
||||
$apiLabel = implode(' ', $apiLabelParts);
|
||||
?>
|
||||
|
||||
<option value="<?php echo (int)$apiConfig['id']; ?>">
|
||||
<?php echo htmlspecialchars($apiLabel, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<small class="text-muted">
|
||||
Select the API/JSON configuration linked to this template.
|
||||
</small>
|
||||
<input type="text" name="start_column" class="form-control" required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
@@ -154,6 +73,7 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
<input type="hidden" name="target_table" value="datadb">
|
||||
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Button Size</label>
|
||||
<select name="button_size" class="form-control">
|
||||
@@ -165,19 +85,19 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Button Background Color</label>
|
||||
<input type="color" name="button_bg_color" class="form-control form-control-color" value="#007bff">
|
||||
<input type="color" name="button_bg_color" class="form-control" value="#007bff">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Button Text Color</label>
|
||||
<input type="color" name="button_text_color" class="form-control form-control-color" value="#ffffff">
|
||||
<input type="color" name="button_text_color" class="form-control" value="#ffffff">
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Button Label</label>
|
||||
<input type="text" name="button_label" class="form-control" value="Click Me">
|
||||
</div>
|
||||
|
||||
<!-- Aggiungi il campo per selezionare il cliente -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Select Client *</label>
|
||||
<select name="client_id" id="clientSelect" class="form-control" required>
|
||||
@@ -185,34 +105,68 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
</select>
|
||||
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Recupero clienti in corso...</span>
|
||||
</div>
|
||||
|
||||
<!-- Aggiungi il campo per selezionare lo schema -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Select Schema *</label>
|
||||
<select name="schema_id" id="schemaSelect" class="form-control" required>
|
||||
<option value="">Select a schema...</option>
|
||||
</select>
|
||||
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Caricamento schemi in corso...</span>
|
||||
<span id="schemaLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Loading schemas...</span>
|
||||
</div>
|
||||
|
||||
<!-- new section for specific client field -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label">Select Routine</label>
|
||||
<select name="idroutine" id="routineSelect" class="form-control">
|
||||
<option value="">Select a routine...</option>
|
||||
<?php foreach ($routines as $routine): ?>
|
||||
<option value="<?php echo $routine['idroutine']; ?>">
|
||||
<?php echo htmlspecialchars($routine['name']); ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<label class="form-label">Client-Specific Fields</label>
|
||||
|
||||
<div id="routineDetails" class="mt-2" style="display: none;">
|
||||
<h6>Routine Details</h6>
|
||||
<p><strong>Name:</strong> <span id="routineName"></span></p>
|
||||
<p><strong>Description:</strong> <span id="routineDescription"></span></p>
|
||||
<p><strong>Action 1:</strong> <span id="routineAction1"></span></p>
|
||||
<p><strong>Action 2:</strong> <span id="routineAction2"></span></p>
|
||||
<p><strong>Action 3:</strong> <span id="routineAction3"></span></p>
|
||||
<!-- Intestazioni -->
|
||||
<div class="row fw-bold text-secondary mb-1">
|
||||
<div class="col-md-3">Field Name</div>
|
||||
<div class="col-md-2">Type</div>
|
||||
<div class="col-md-2 dropdown-values">Possible Values</div>
|
||||
<div class="col-md-1">Required</div>
|
||||
<div class="col-md-2">Export Column Name</div>
|
||||
<div class="col-md-1">Default Value</div>
|
||||
<div class="col-md-1">Actions</div>
|
||||
</div>
|
||||
|
||||
<div id="clientSpecificFields">
|
||||
<!-- Contenitore per i campi dinamici -->
|
||||
<div class="client-field-row mb-2">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="specific_fields[0][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="specific_fields[0][type]" class="form-control" onchange="toggleDropdownValues(this)">
|
||||
<option value="text">Text</option>
|
||||
<option value="dropdown">Dropdown</option>
|
||||
<option value="date">Date</option>
|
||||
<option value="boolean">Yes/No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 dropdown-values" style="visibility: hidden;">
|
||||
<input type="text" name="specific_fields[0][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<select name="specific_fields[0][required]" class="form-control">
|
||||
<option value="1">Yes</option>
|
||||
<option value="0">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="text" name="specific_fields[0][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<input type="text" name="specific_fields[0][default_value]" class="form-control" placeholder="Default Value (optional)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="button" class="btn btn-danger remove-field">-</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn btn-primary mt-2" id="addField">Add Field</button>
|
||||
</div>
|
||||
|
||||
<br>
|
||||
@@ -222,122 +176,84 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--end page wrapper -->
|
||||
<!--start overlay-->
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<!--end overlay-->
|
||||
<!--Start Back To Top Button-->
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<!--End Back To Top Button-->
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<!--end wrapper-->
|
||||
|
||||
<!-- search modal -->
|
||||
<?php //include('include/searchmodal.php');
|
||||
?>
|
||||
<!-- end search modal -->
|
||||
|
||||
<!--start switcher-->
|
||||
<?php //include('include/themeswitcher.php');
|
||||
?>
|
||||
<!--end switcher-->
|
||||
|
||||
<!-- Includi Select2 JS -->
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
<script>
|
||||
// Debug iniziale
|
||||
console.log("JavaScript is loaded and running!");
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function() {
|
||||
if (typeof jQuery === 'undefined') {
|
||||
alert("Errore: jQuery non è caricato. Contatta l'amministratore.");
|
||||
return;
|
||||
}
|
||||
console.log("DOM is loaded");
|
||||
|
||||
const form = document.getElementById("insertTemplateForm");
|
||||
const addFieldButton = document.getElementById("addField");
|
||||
const container = document.getElementById("clientSpecificFields");
|
||||
const clientLoadingStatus = document.getElementById("clientLoadingStatus");
|
||||
const schemaLoadingStatus = document.getElementById("schemaLoadingStatus");
|
||||
const routineSelect = document.getElementById("routineSelect");
|
||||
const routineDetails = document.getElementById("routineDetails");
|
||||
const routineName = document.getElementById("routineName");
|
||||
const routineDescription = document.getElementById("routineDescription");
|
||||
const routineAction1 = document.getElementById("routineAction1");
|
||||
const routineAction2 = document.getElementById("routineAction2");
|
||||
const routineAction3 = document.getElementById("routineAction3");
|
||||
|
||||
const sourceType = document.getElementById("sourceType");
|
||||
|
||||
const headerRowWrapper = document.getElementById("headerRowWrapper");
|
||||
const startColumnWrapper = document.getElementById("startColumnWrapper");
|
||||
const xlsSheetNumberWrapper = document.getElementById("xlsSheetNumberWrapper");
|
||||
const apiConfigWrapper = document.getElementById("apiConfigWrapper");
|
||||
|
||||
const headerRow = document.getElementById("headerRow");
|
||||
const startColumn = document.getElementById("startColumn");
|
||||
const xlsSheetIndex = document.getElementById("xlsSheetIndex");
|
||||
const apiConfigSelect = document.getElementById("apiConfigSelect");
|
||||
|
||||
if (!form || !clientLoadingStatus || !schemaLoadingStatus || !routineSelect || !routineDetails) {
|
||||
alert("Errore: Uno o più elementi della pagina non sono stati trovati. Contatta l'amministratore.");
|
||||
if (!form || !addFieldButton || !container || !clientLoadingStatus || !schemaLoadingStatus) {
|
||||
console.error("One or more DOM elements not found:", {
|
||||
form,
|
||||
addFieldButton,
|
||||
container,
|
||||
clientLoadingStatus,
|
||||
schemaLoadingStatus
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("All DOM elements found");
|
||||
|
||||
// Controllo che jQuery sia caricato
|
||||
if (typeof jQuery === 'undefined') {
|
||||
console.error("jQuery non è caricato!");
|
||||
return;
|
||||
}
|
||||
|
||||
// Inizializza Select2 sulla tendina dei clienti
|
||||
$('#clientSelect').select2({
|
||||
placeholder: "Search for a client...",
|
||||
allowClear: true
|
||||
}).on('select2:open', function() {
|
||||
console.log("Select2 initialized successfully for clientSelect");
|
||||
}).on('select2:select', function(e) {
|
||||
console.log("Client selected:", e.params.data.id, e.params.data.text);
|
||||
});
|
||||
|
||||
// Inizializza Select2 sulla tendina degli schemi
|
||||
$('#schemaSelect').select2({
|
||||
placeholder: "Search for a schema...",
|
||||
allowClear: true
|
||||
}).on('select2:open', function() {
|
||||
console.log("Select2 initialized successfully for schemaSelect");
|
||||
});
|
||||
|
||||
$('#routineSelect').select2({
|
||||
placeholder: "Select a routine...",
|
||||
allowClear: true
|
||||
});
|
||||
|
||||
$('#apiConfigSelect').select2({
|
||||
placeholder: "Select an API configuration...",
|
||||
allowClear: true
|
||||
});
|
||||
|
||||
function updateSourceFields() {
|
||||
const selectedSource = sourceType.value;
|
||||
|
||||
const isXls = selectedSource === 'XLS';
|
||||
const isApiJson = selectedSource === 'API';
|
||||
|
||||
if (isXls) {
|
||||
headerRowWrapper.style.display = 'block';
|
||||
startColumnWrapper.style.display = 'block';
|
||||
xlsSheetNumberWrapper.style.display = 'block';
|
||||
|
||||
headerRow.required = true;
|
||||
startColumn.required = true;
|
||||
xlsSheetIndex.required = true;
|
||||
|
||||
headerRow.disabled = false;
|
||||
startColumn.disabled = false;
|
||||
xlsSheetIndex.disabled = false;
|
||||
|
||||
apiConfigWrapper.style.display = 'none';
|
||||
apiConfigSelect.required = false;
|
||||
apiConfigSelect.disabled = true;
|
||||
$('#apiConfigSelect').val(null).trigger('change');
|
||||
} else {
|
||||
headerRowWrapper.style.display = 'none';
|
||||
startColumnWrapper.style.display = 'none';
|
||||
xlsSheetNumberWrapper.style.display = 'none';
|
||||
|
||||
headerRow.required = false;
|
||||
startColumn.required = false;
|
||||
xlsSheetIndex.required = false;
|
||||
|
||||
headerRow.disabled = true;
|
||||
startColumn.disabled = true;
|
||||
xlsSheetIndex.disabled = true;
|
||||
|
||||
if (isApiJson) {
|
||||
apiConfigWrapper.style.display = 'block';
|
||||
apiConfigSelect.required = true;
|
||||
apiConfigSelect.disabled = false;
|
||||
} else {
|
||||
apiConfigWrapper.style.display = 'none';
|
||||
apiConfigSelect.required = false;
|
||||
apiConfigSelect.disabled = true;
|
||||
$('#apiConfigSelect').val(null).trigger('change');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sourceType.addEventListener('change', updateSourceFields);
|
||||
updateSourceFields();
|
||||
|
||||
// Funzione per caricare i clienti
|
||||
async function loadClients() {
|
||||
try {
|
||||
clientLoadingStatus.style.display = 'inline';
|
||||
@@ -350,32 +266,38 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
const text = await response.text();
|
||||
console.log("Risposta raw (clienti):", text);
|
||||
const data = JSON.parse(text);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||
throw new Error(data.error || `Errore HTTP: ${response.status}, Dettagli: ${JSON.stringify(data)}`);
|
||||
}
|
||||
|
||||
const select = document.getElementById("clientSelect");
|
||||
select.innerHTML = '<option value="">Select a client...</option>';
|
||||
|
||||
data.value.forEach(client => {
|
||||
const nome = client.Nominativo || "Nome non disponibile";
|
||||
const id = client.IdCliente || "ID non disponibile";
|
||||
|
||||
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? "").toString().trim();
|
||||
const suffix = (codiceCliente.split("_")[1] || "").trim();
|
||||
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : "--");
|
||||
|
||||
const option = new Option(`${nome.trim()} - ${shortCode} (ID: ${id})`, id);
|
||||
select.add(option);
|
||||
});
|
||||
|
||||
$(select).trigger('change');
|
||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||
if (data.value && Array.isArray(data.value)) {
|
||||
const select = document.getElementById("clientSelect");
|
||||
select.innerHTML = '<option value="">Select a client...</option>';
|
||||
data.value.forEach(client => {
|
||||
const nome = client.Nominativo || "Nome non disponibile";
|
||||
const id = client.IdCliente || "ID non disponibile";
|
||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||
select.add(option);
|
||||
});
|
||||
console.log("Clienti caricati con successo.");
|
||||
clientLoadingStatus.textContent = "Clienti caricati.";
|
||||
} else {
|
||||
console.error("Nessun cliente trovato o formato dati non valido.", data);
|
||||
clientLoadingStatus.textContent = "Nessun cliente trovato.";
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Nessun cliente trovato o formato dati non valido.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Errore nel caricamento dei clienti:", error);
|
||||
clientLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare i clienti: " + error.message,
|
||||
@@ -383,153 +305,175 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 2000);
|
||||
setTimeout(() => {
|
||||
clientLoadingStatus.style.display = 'none';
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
|
||||
// Funzione per caricare gli schemi con ritentativi
|
||||
async function loadSchemas() {
|
||||
try {
|
||||
schemaLoadingStatus.style.display = 'inline';
|
||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
||||
const maxRetries = 3;
|
||||
let attempt = 0;
|
||||
|
||||
const response = await fetch("get_schemi.php", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
while (attempt < maxRetries) {
|
||||
try {
|
||||
schemaLoadingStatus.style.display = 'inline';
|
||||
schemaLoadingStatus.textContent = 'Caricamento schemi in corso...';
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || `Errore HTTP: ${response.status}`);
|
||||
}
|
||||
|
||||
const select = document.getElementById("schemaSelect");
|
||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||
|
||||
const sortedSchemas = [...data.value].sort((a, b) => {
|
||||
const nomeA = (a.Nome || "").trim().toLowerCase();
|
||||
const nomeB = (b.Nome || "").trim().toLowerCase();
|
||||
return nomeA.localeCompare(nomeB, 'it', {
|
||||
sensitivity: 'base'
|
||||
const response = await fetch("get_schemi.php", {
|
||||
method: "GET",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
sortedSchemas.forEach(schema => {
|
||||
const nome = schema.Nome || "Nome non disponibile";
|
||||
const id = schema.IdSchemaCustomFields || "ID non disponibile";
|
||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
||||
select.add(option);
|
||||
});
|
||||
const text = await response.text();
|
||||
console.log("Risposta raw (schemi):", text);
|
||||
const data = JSON.parse(text);
|
||||
|
||||
$(select).trigger('change');
|
||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
||||
} catch (error) {
|
||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
if (!response.ok) {
|
||||
throw new Error(data.error || `Errore HTTP: ${response.status}, Dettagli: ${JSON.stringify(data)}`);
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare gli schemi: " + error.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
} finally {
|
||||
setTimeout(() => schemaLoadingStatus.style.display = 'none', 2000);
|
||||
const select = document.getElementById("schemaSelect");
|
||||
select.innerHTML = '<option value="">Select a schema...</option>';
|
||||
data.value.forEach(schema => { // Nota: usa data.value per coerenza con il JSON restituito
|
||||
const option = new Option(`${schema.Nome} (ID: ${schema.IdSchemaCustomFields})`, schema.IdSchemaCustomFields);
|
||||
select.add(option);
|
||||
});
|
||||
|
||||
schemaLoadingStatus.textContent = "Schemi caricati.";
|
||||
break; // Esci dal ciclo se la chiamata ha successo
|
||||
} catch (error) {
|
||||
attempt++;
|
||||
console.error(`Tentativo ${attempt} fallito per schemi:`, error);
|
||||
if (attempt === maxRetries) {
|
||||
console.error("Errore finale nel caricamento degli schemi:", error);
|
||||
schemaLoadingStatus.textContent = "Errore nel caricamento.";
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Impossibile caricare gli schemi: " + error.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
} else {
|
||||
// Ritardo prima di riprovare
|
||||
await new Promise(resolve => setTimeout(resolve, 1000 * attempt));
|
||||
}
|
||||
} finally {
|
||||
if (attempt === maxRetries || schemaLoadingStatus.textContent === "Schemi caricati.") {
|
||||
setTimeout(() => {
|
||||
schemaLoadingStatus.style.display = 'none';
|
||||
}, 2000);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Funzione combinata per caricare i dati in sequenza
|
||||
async function loadData() {
|
||||
try {
|
||||
await loadClients();
|
||||
await loadSchemas();
|
||||
await loadClients(); // Carica prima i clienti
|
||||
await loadSchemas(); // Poi carica gli schemi
|
||||
} catch (error) {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Errore nel caricamento dei dati: " + error.message,
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
console.error("Errore nel caricamento dei dati:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// Avvia il caricamento dei dati
|
||||
loadData();
|
||||
|
||||
// Gestione dinamica dei campi specifici
|
||||
addFieldButton.addEventListener("click", function() {
|
||||
console.log("Add Field button clicked");
|
||||
const fieldCount = container.getElementsByClassName("client-field-row").length;
|
||||
const newField = document.createElement("div");
|
||||
newField.className = "client-field-row mb-2";
|
||||
newField.innerHTML = `
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<input type="text" name="specific_fields[${fieldCount}][name]" class="form-control" placeholder="Field Name (e.g., SKU)">
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<select name="specific_fields[${fieldCount}][type]" class="form-control" onchange="toggleDropdownValues(this)">
|
||||
<option value="text">Text</option>
|
||||
<option value="dropdown">Dropdown</option>
|
||||
<option value="date">Date</option>
|
||||
<option value="boolean">Yes/No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2 dropdown-values" style="visibility: hidden;">
|
||||
<input type="text" name="specific_fields[${fieldCount}][possible_values]" class="form-control" placeholder="Values (e.g., Red, Blue, Green)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<select name="specific_fields[${fieldCount}][required]" class="form-control">
|
||||
<option value="1">Yes</option>
|
||||
<option value="0">No</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-2">
|
||||
<input type="text" name="specific_fields[${fieldCount}][export_column_name]" class="form-control" placeholder="Export Column Name (e.g., MONCLER_SKU)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<input type="text" name="specific_fields[${fieldCount}][default_value]" class="form-control" placeholder="Default Value (optional)">
|
||||
</div>
|
||||
<div class="col-md-1">
|
||||
<button type="button" class="btn btn-danger remove-field">-</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
container.appendChild(newField);
|
||||
|
||||
newField.querySelector(".remove-field").addEventListener("click", function() {
|
||||
console.log("Remove Field button clicked");
|
||||
container.removeChild(newField);
|
||||
updateFieldIndices();
|
||||
});
|
||||
});
|
||||
|
||||
window.toggleDropdownValues = function(selectElement) {
|
||||
console.log("Toggling dropdown values for:", selectElement.value);
|
||||
const row = selectElement.closest(".row");
|
||||
const dropdownValues = row.querySelector(".dropdown-values");
|
||||
if (selectElement.value === "dropdown") {
|
||||
dropdownValues.style.visibility = "visible";
|
||||
} else {
|
||||
dropdownValues.style.visibility = "hidden";
|
||||
}
|
||||
};
|
||||
|
||||
document.querySelectorAll(".remove-field").forEach(button => {
|
||||
button.addEventListener("click", function() {
|
||||
console.log("Existing remove button clicked");
|
||||
const container = document.getElementById("clientSpecificFields");
|
||||
container.removeChild(button.closest(".client-field-row"));
|
||||
updateFieldIndices();
|
||||
});
|
||||
});
|
||||
|
||||
function updateFieldIndices() {
|
||||
console.log("Updating field indices");
|
||||
const rows = container.getElementsByClassName("client-field-row");
|
||||
for (let i = 0; i < rows.length; i++) {
|
||||
const inputs = rows[i].querySelectorAll("input, select");
|
||||
inputs.forEach(input => {
|
||||
const name = input.name.replace(/\[\d+\]/, `[${i}]`);
|
||||
input.name = name;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
loadData();
|
||||
|
||||
const routines = <?php echo json_encode($routines); ?>;
|
||||
|
||||
function updateRoutineDetails() {
|
||||
const selectedId = routineSelect.value;
|
||||
routineDetails.style.display = selectedId ? 'block' : 'none';
|
||||
|
||||
if (selectedId) {
|
||||
const routine = routines.find(r => r.idroutine == selectedId);
|
||||
|
||||
if (routine) {
|
||||
routineName.textContent = routine.name || 'N/A';
|
||||
routineDescription.textContent = routine.description || 'N/A';
|
||||
routineAction1.textContent = routine.action1 || 'N/A';
|
||||
routineAction2.textContent = routine.action2 || 'N/A';
|
||||
routineAction3.textContent = routine.action3 || 'N/A';
|
||||
} else {
|
||||
routineName.textContent = 'N/A';
|
||||
routineDescription.textContent = 'N/A';
|
||||
routineAction1.textContent = 'N/A';
|
||||
routineAction2.textContent = 'N/A';
|
||||
routineAction3.textContent = 'N/A';
|
||||
}
|
||||
} else {
|
||||
routineName.textContent = '';
|
||||
routineDescription.textContent = '';
|
||||
routineAction1.textContent = '';
|
||||
routineAction2.textContent = '';
|
||||
routineAction3.textContent = '';
|
||||
}
|
||||
}
|
||||
|
||||
routineSelect.addEventListener('change', updateRoutineDetails);
|
||||
updateRoutineDetails();
|
||||
|
||||
form.addEventListener("submit", function(e) {
|
||||
e.preventDefault();
|
||||
console.log("Form submitted");
|
||||
|
||||
let formData = new FormData(this);
|
||||
|
||||
const selectedSource = sourceType.value;
|
||||
|
||||
if (selectedSource === 'XLS' && xlsSheetIndex.value === '') {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Inserisci il numero del foglio XLS.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedSource === 'API' && !apiConfigSelect.value) {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Seleziona una configurazione API / JSON.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Aggiungi il nome del cliente selezionato a FormData
|
||||
const clientSelect = document.getElementById("clientSelect");
|
||||
const clientId = clientSelect.value;
|
||||
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
|
||||
|
||||
if (!clientId) {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Per favore seleziona un cliente.",
|
||||
icon: "error",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
let clientName = "";
|
||||
if (selectedClientOption) {
|
||||
const optionText = selectedClientOption.text.trim();
|
||||
@@ -538,11 +482,18 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
formData.append("client_name", clientName);
|
||||
|
||||
// Aggiungi l'ID e il nome dello schema selezionato a FormData
|
||||
const schemaSelect = document.getElementById("schemaSelect");
|
||||
const schemaId = schemaSelect.value;
|
||||
const selectedSchemaOption = schemaSelect.options[schemaSelect.selectedIndex];
|
||||
|
||||
if (!schemaId) {
|
||||
let schemaName = "";
|
||||
if (selectedSchemaOption && schemaId) {
|
||||
const optionText = selectedSchemaOption.text.trim();
|
||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||
formData.append("idschema", schemaId);
|
||||
formData.append("schemamaname", schemaName);
|
||||
} else {
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Per favore seleziona uno schema.",
|
||||
@@ -552,18 +503,48 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
return;
|
||||
}
|
||||
|
||||
let schemaName = "";
|
||||
if (selectedSchemaOption) {
|
||||
const optionText = selectedSchemaOption.text.trim();
|
||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
||||
schemaName = nameMatch ? nameMatch[1].trim() : optionText;
|
||||
// Log per debug
|
||||
console.log("Client ID:", clientId);
|
||||
console.log("Client Name:", clientName);
|
||||
console.log("Schema ID:", schemaId);
|
||||
console.log("Schema Name:", schemaName);
|
||||
|
||||
// Genera il JSON per client_specific_fields
|
||||
let finalSpecificFields = {};
|
||||
const fieldRows = container.getElementsByClassName("client-field-row");
|
||||
for (let i = 0; i < fieldRows.length; i++) {
|
||||
const row = fieldRows[i];
|
||||
const inputs = row.querySelectorAll("input, select");
|
||||
let fieldData = {};
|
||||
|
||||
inputs.forEach(input => {
|
||||
const nameMatch = input.name.match(/specific_fields\[\d+\]\[(.*?)\]/);
|
||||
if (nameMatch) {
|
||||
const fieldName = nameMatch[1];
|
||||
fieldData[fieldName] = input.value.trim();
|
||||
}
|
||||
});
|
||||
|
||||
if (fieldData.name) {
|
||||
finalSpecificFields[fieldData.name] = {
|
||||
type: fieldData.type || "text",
|
||||
possible_values: (fieldData.possible_values && fieldData.type === "dropdown") ? fieldData.possible_values.split(",").map(v => v.trim()) : [],
|
||||
is_required: fieldData.required === "1",
|
||||
export_column_name: fieldData.export_column_name || "",
|
||||
default_value: fieldData.default_value || ""
|
||||
};
|
||||
console.log(`Field ${fieldData.name}:`, finalSpecificFields[fieldData.name]);
|
||||
}
|
||||
}
|
||||
|
||||
formData.append("idschema", schemaId);
|
||||
formData.append("schemaname", schemaName);
|
||||
console.log("Generated JSON for client_specific_fields:", JSON.stringify(finalSpecificFields));
|
||||
formData.append("client_specific_fields", JSON.stringify(finalSpecificFields));
|
||||
|
||||
const routineId = routineSelect.value;
|
||||
formData.append("idroutine", routineId);
|
||||
// Debug del FormData
|
||||
console.log("FormData contents:");
|
||||
for (let pair of formData.entries()) {
|
||||
console.log(pair[0] + ': ' + pair[1]);
|
||||
}
|
||||
|
||||
fetch("process_insert_template_xls.php", {
|
||||
method: "POST",
|
||||
@@ -571,6 +552,7 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
console.log("Fetch response:", data);
|
||||
if (data.success) {
|
||||
Swal.fire({
|
||||
title: "Successo!",
|
||||
@@ -590,6 +572,7 @@ $apiConfigurations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Errore Fetch:", error);
|
||||
Swal.fire({
|
||||
title: "Errore!",
|
||||
text: "Si è verificato un errore imprevisto.",
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
require_once 'class/db-functions.php';
|
||||
|
||||
$response = ["success" => false, "data" => [], "message" => ""];
|
||||
@@ -13,25 +12,8 @@ try {
|
||||
throw new Exception('Database connection failed.');
|
||||
}
|
||||
|
||||
$userId = $iduserlogin ?? 0;
|
||||
|
||||
// Recupera i template attivi, con flag is_favorite per l'utente corrente
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
et.id,
|
||||
et.button_label,
|
||||
et.button_size,
|
||||
et.button_bg_color,
|
||||
et.button_text_color,
|
||||
et.source_type,
|
||||
CASE WHEN uf.id IS NOT NULL THEN 1 ELSE 0 END AS is_favorite
|
||||
FROM excel_templates et
|
||||
LEFT JOIN user_template_favorites uf
|
||||
ON uf.template_id = et.id AND uf.user_id = :user_id
|
||||
WHERE et.status = 'active'
|
||||
ORDER BY et.button_label ASC
|
||||
");
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
// Recupera solo i template attivi
|
||||
$stmt = $pdo->query("SELECT id, button_label, button_bg_color, button_text_color, button_size FROM excel_templates WHERE status = 'active'");
|
||||
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$response["success"] = true;
|
||||
|
||||
@@ -14,57 +14,11 @@ if (!$iddatadb) {
|
||||
}
|
||||
|
||||
try {
|
||||
// 1) prendo templateid da datadb
|
||||
$stmtTpl = $pdo->prepare("SELECT templateid FROM datadb WHERE iddatadb = :iddatadb LIMIT 1");
|
||||
$stmtTpl->execute([':iddatadb' => $iddatadb]);
|
||||
$templateid = $stmtTpl->fetchColumn();
|
||||
$stmt = $pdo->prepare("SELECT id, iddatadb, part_number, part_description FROM identification_parts WHERE iddatadb = :iddatadb ORDER BY part_number ASC");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
$parts = $stmt->fetchAll();
|
||||
|
||||
// 2) prendo (max 1) field_id visibile in parts
|
||||
$extraFieldId = null;
|
||||
if ($templateid) {
|
||||
$stmtEF = $pdo->prepare("SELECT field_id FROM template_mapping WHERE template_id = :templateid AND is_visible_parts = 1 ORDER BY id ASC LIMIT 1");
|
||||
$stmtEF->execute([':templateid' => $templateid]);
|
||||
$extraFieldId = $stmtEF->fetchColumn();
|
||||
if ($extraFieldId !== false) $extraFieldId = (int)$extraFieldId;
|
||||
else $extraFieldId = null;
|
||||
}
|
||||
|
||||
// 3) carico parts + join su tabella figlia
|
||||
if ($extraFieldId) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
p.id, p.iddatadb, p.part_number, p.part_description, p.mix, p.idmatrice, p.note, p.dateexpiry,
|
||||
cf.value_id AS extra_value_id,
|
||||
cf.value_text AS extra_value_text
|
||||
FROM identification_parts p
|
||||
LEFT JOIN identification_parts_customfields cf
|
||||
ON cf.part_id = p.id AND cf.field_id = :extraFieldId
|
||||
WHERE p.iddatadb = :iddatadb
|
||||
ORDER BY p.part_number ASC
|
||||
");
|
||||
$stmt->execute([
|
||||
':iddatadb' => $iddatadb,
|
||||
':extraFieldId' => $extraFieldId
|
||||
]);
|
||||
} else {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id, iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry,
|
||||
NULL AS extra_value_id, NULL AS extra_value_text
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
ORDER BY part_number ASC
|
||||
");
|
||||
$stmt->execute([':iddatadb' => $iddatadb]);
|
||||
}
|
||||
|
||||
$parts = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'parts' => $parts,
|
||||
'extra_field_id' => $extraFieldId,
|
||||
'debug_sql' => ($extraFieldId ? 'WITH_CF_JOIN' : 'NO_CF')
|
||||
]);
|
||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
$idquotations = $_GET['idquotations'] ?? null;
|
||||
|
||||
if (!$idquotations) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID quotations mancante']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare("SELECT id, idquotations, part_number, part_description FROM identification_parts WHERE idquotations = :idquotations ORDER BY part_number ASC");
|
||||
$stmt->execute([':idquotations' => $idquotations]);
|
||||
$parts = $stmt->fetchAll();
|
||||
|
||||
echo json_encode(['success' => true, 'parts' => $parts]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
<?php
|
||||
// load_photo_quotation.php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
$idquotations = isset($_GET['idquotations']) ? intval($_GET['idquotations']) : null;
|
||||
|
||||
if (!$idquotations) {
|
||||
echo json_encode(['success' => false, 'message' => 'ID quotation mancante']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// Seleziona le foto per il dato idquotations dalla tabella datadb_photos
|
||||
$stmt = $pdo->prepare("SELECT id, file_path FROM datadb_photos WHERE idquotations = ?");
|
||||
$stmt->execute([$idquotations]);
|
||||
$photos = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
if ($photos && count($photos) > 0) {
|
||||
$photoPaths = array_map(function ($photo) {
|
||||
return '../photostrf/' . $photo['file_path'];
|
||||
}, $photos);
|
||||
echo json_encode(['success' => true, 'photos' => $photoPaths]);
|
||||
} else {
|
||||
echo json_encode(['success' => false, 'message' => 'Nessuna foto trovata']);
|
||||
}
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
|
||||
include('include/headscript.php');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
// Recupera l'ID dell'utente loggato
|
||||
$user_id = $iduserlogin ?? 1;
|
||||
|
||||
if (!$user_id) {
|
||||
echo json_encode(['success' => false, 'message' => "ID dell'utente autenticato mancante"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT DISTINCT q.*
|
||||
FROM quotations q
|
||||
INNER JOIN identification_parts ip
|
||||
ON ip.idquotations = q.id
|
||||
AND ip.iddatadb IS NULL
|
||||
WHERE q.iduser = :iduser"
|
||||
);
|
||||
$stmt->execute([':iduser' => $user_id]);
|
||||
$quotations = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
echo json_encode(['success' => true, 'quotations' => $quotations]);
|
||||
} catch (PDOException $e) {
|
||||
echo json_encode(['success' => false, 'message' => 'Errore nel caricamento delle quotations: ' . $e->getMessage()]);
|
||||
}
|
||||
@@ -1,60 +0,0 @@
|
||||
CAMPIONE #0
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 563528,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"ConsegnaRichiesta": "2026-01-30"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 734582,
|
||||
"CodiceCampione": "10978",
|
||||
"CodiceCampioneWeb": "10978",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:02.1595894+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-30T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "Parte AAA",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
CAMPIONE #1
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"Commessa": 563528,
|
||||
"Matrice": 3879,
|
||||
"SottoMatrice": null,
|
||||
"SchemaCustomField": 82,
|
||||
"NoteWeb": "PARE BBB",
|
||||
"ConsegnaRichiesta": "2026-01-30"
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#Campione\/$entity",
|
||||
"IdCampione": 734583,
|
||||
"CodiceCampione": "10979",
|
||||
"CodiceCampioneWeb": "10979",
|
||||
"StatoCampione": "Nuovo",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:03.9972635+01:00",
|
||||
"RiferimentoWeb": "",
|
||||
"ConsegnaRichiesta": "2026-01-30T00:00:00+01:00",
|
||||
"DataAccettazioneLims": null,
|
||||
"Riferimento": null,
|
||||
"NoteWeb": "PARE BBB",
|
||||
"GruppiRicercati": null
|
||||
}
|
||||
|
||||
---
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,20 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)/ImportaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{
|
||||
"IdUtente": 285
|
||||
}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Elaborata",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.897+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": "2026-03-11T14:10:11.76+01:00",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
Photos for CommessaWeb 563528 (iddatadb=1259):
|
||||
Total photos found: 2, campioni: 2
|
||||
|
||||
=== Campione 734582 (main) ===
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(734582)/UploadCampioneFile' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1259-20260311130930-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||
"IdCampioneFile": 1779561,
|
||||
"FileName": "1259-20260311130930-104a2ca5-8a4c-4df8-b2ca-a28f518b7b25.jpg",
|
||||
"Web": false,
|
||||
"AllegaAlRapporto": false,
|
||||
"StampaNelRapporto": false,
|
||||
"ReportGermania": false,
|
||||
"PrimaPagina": false,
|
||||
"Duplica": false,
|
||||
"LastUpdate": "2026-03-11T14:10:06.7051549+01:00",
|
||||
"Titolo": null
|
||||
}
|
||||
|
||||
---
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/Campione(734582)/UploadCampioneFile' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--form 'file=@C:\xampp\htdocs\trf_certest\public\photostrf\1259-20260311130930-1e672dd9-5420-4432-b422-02d8d271c178.jpg'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CampioneFiles\/$entity",
|
||||
"IdCampioneFile": 1779562,
|
||||
"FileName": "1259-20260311130930-1e672dd9-5420-4432-b422-02d8d271c178.jpg",
|
||||
"Web": false,
|
||||
"AllegaAlRapporto": false,
|
||||
"StampaNelRapporto": false,
|
||||
"ReportGermania": false,
|
||||
"PrimaPagina": false,
|
||||
"Duplica": false,
|
||||
"LastUpdate": "2026-03-11T14:10:08.5442313+01:00",
|
||||
"Titolo": null
|
||||
}
|
||||
|
||||
---
|
||||
@@ -1,18 +0,0 @@
|
||||
curl --location --request POST 'https://93.43.5.102/limsapi/api/odata/CommessaWeb(563528)/InviaCommessa' \
|
||||
--header 'Content-Type: application/json' \
|
||||
--header 'Authorization: Bearer ••••••' \
|
||||
--data '{}'
|
||||
|
||||
RESPONSE:
|
||||
{
|
||||
"@odata.context": "https:\/\/bvcpsitaly-elims.com\/limsapi\/api\/odata\/$metadata#CommessaWeb\/$entity",
|
||||
"IdCommessa": 563528,
|
||||
"CodiceCommessa": "26C0029",
|
||||
"RiferimentoCertificato": null,
|
||||
"StatoCommessaWeb": "Nuova",
|
||||
"DataCreazioneWeb": "2026-03-11T14:10:00.897+01:00",
|
||||
"CodiceCommessaWeb": "26C0029",
|
||||
"DataInviatoWeb": "2026-03-11T14:10:11.7602299+01:00",
|
||||
"Richiedente": "Test Web Import",
|
||||
"Descrizione": "TEST CommessaWeb"
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user