Compare commits
2 Commits
dab8d9aebf
...
57a7598261
| Author | SHA1 | Date | |
|---|---|---|---|
| 57a7598261 | |||
| d83b768b36 |
@@ -44,6 +44,7 @@
|
|||||||
"phpmailer/phpmailer": "^6.9",
|
"phpmailer/phpmailer": "^6.9",
|
||||||
"phpoffice/phpspreadsheet": "^4.1",
|
"phpoffice/phpspreadsheet": "^4.1",
|
||||||
"proengsoft/laravel-jsvalidation": "^4.0.0",
|
"proengsoft/laravel-jsvalidation": "^4.0.0",
|
||||||
|
"robmorgan/phinx": "^0.16.11",
|
||||||
"socialiteproviders/microsoft": "^4.7",
|
"socialiteproviders/microsoft": "^4.7",
|
||||||
"spatie/laravel-query-builder": "^5.0",
|
"spatie/laravel-query-builder": "^5.0",
|
||||||
"vanguardapp/activity-log": "^6.0",
|
"vanguardapp/activity-log": "^6.0",
|
||||||
|
|||||||
Generated
+646
-2
@@ -4,7 +4,7 @@
|
|||||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||||
"This file is @generated automatically"
|
"This file is @generated automatically"
|
||||||
],
|
],
|
||||||
"content-hash": "9c4f1e3bc3ee2180211c055e70635aef",
|
"content-hash": "076e7721d08cfea8b06ce75dd8c6c576",
|
||||||
"packages": [
|
"packages": [
|
||||||
{
|
{
|
||||||
"name": "akaunting/laravel-setting",
|
"name": "akaunting/laravel-setting",
|
||||||
@@ -251,6 +251,330 @@
|
|||||||
],
|
],
|
||||||
"time": "2023-11-29T23:19:16+00:00"
|
"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",
|
"name": "carbonphp/carbon-doctrine-types",
|
||||||
"version": "3.2.0",
|
"version": "3.2.0",
|
||||||
@@ -2627,6 +2951,90 @@
|
|||||||
],
|
],
|
||||||
"time": "2022-12-11T20:36:23+00:00"
|
"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",
|
"name": "league/flysystem",
|
||||||
"version": "3.28.0",
|
"version": "3.28.0",
|
||||||
@@ -4980,6 +5388,93 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-04-27T21:32:50+00:00"
|
"time": "2024-04-27T21:32:50+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "robmorgan/phinx",
|
||||||
|
"version": "0.16.11",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/cakephp/phinx.git",
|
||||||
|
"reference": "a03014fea316ba021fc0776982e5bed2d10228d4"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/cakephp/phinx/zipball/a03014fea316ba021fc0776982e5bed2d10228d4",
|
||||||
|
"reference": "a03014fea316ba021fc0776982e5bed2d10228d4",
|
||||||
|
"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.11"
|
||||||
|
},
|
||||||
|
"time": "2026-03-15T00:04:32+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "socialiteproviders/manager",
|
"name": "socialiteproviders/manager",
|
||||||
"version": "v4.8.1",
|
"version": "v4.8.1",
|
||||||
@@ -5312,6 +5807,85 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-05-31T14:57:53+00:00"
|
"time": "2024-05-31T14:57:53+00:00"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "symfony/config",
|
||||||
|
"version": "v7.4.10",
|
||||||
|
"source": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/symfony/config.git",
|
||||||
|
"reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57"
|
||||||
|
},
|
||||||
|
"dist": {
|
||||||
|
"type": "zip",
|
||||||
|
"url": "https://api.github.com/repos/symfony/config/zipball/d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57",
|
||||||
|
"reference": "d91b6c7cd2a8c9a9c2b8d26c8f5ed48edf99ef57",
|
||||||
|
"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.10"
|
||||||
|
},
|
||||||
|
"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-03T14:20:49+00:00"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "symfony/console",
|
"name": "symfony/console",
|
||||||
"version": "v7.1.3",
|
"version": "v7.1.3",
|
||||||
@@ -5768,6 +6342,76 @@
|
|||||||
],
|
],
|
||||||
"time": "2024-04-18T09:32:20+00:00"
|
"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",
|
"name": "symfony/finder",
|
||||||
"version": "v7.1.3",
|
"version": "v7.1.3",
|
||||||
@@ -11355,6 +11999,6 @@
|
|||||||
"php": "^8.2.0",
|
"php": "^8.2.0",
|
||||||
"ext-json": "*"
|
"ext-json": "*"
|
||||||
},
|
},
|
||||||
"platform-dev": [],
|
"platform-dev": {},
|
||||||
"plugin-api-version": "2.6.0"
|
"plugin-api-version": "2.6.0"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,192 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class CreateCompanyBrandDepartmentTables extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function change(): void
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Companies table
|
||||||
|
* Main customer/company registry for TRFgo.
|
||||||
|
*/
|
||||||
|
$this->table('companies', ['id' => 'idcompany', 'signed' => false])
|
||||||
|
->addColumn('company_name', 'string', ['limit' => 255])
|
||||||
|
->addColumn('legal_name', 'string', ['limit' => 255, 'null' => true])
|
||||||
|
->addColumn('vat_number', 'string', ['limit' => 50, 'null' => true])
|
||||||
|
->addColumn('external_code', 'string', ['limit' => 100, 'null' => true])
|
||||||
|
->addColumn('address', 'string', ['limit' => 255, 'null' => true])
|
||||||
|
->addColumn('city', 'string', ['limit' => 100, 'null' => true])
|
||||||
|
->addColumn('zip', 'string', ['limit' => 50, 'null' => true])
|
||||||
|
->addColumn('country_id', 'integer', ['signed' => false, 'null' => true])
|
||||||
|
->addColumn('email', 'string', ['limit' => 191, 'null' => true])
|
||||||
|
->addColumn('phone', 'string', ['limit' => 50, 'null' => true])
|
||||||
|
->addColumn('status', 'enum', [
|
||||||
|
'values' => ['active', 'inactive', 'suspended'],
|
||||||
|
'default' => 'active',
|
||||||
|
])
|
||||||
|
->addTimestamps()
|
||||||
|
->addIndex(['company_name'])
|
||||||
|
->addIndex(['external_code'])
|
||||||
|
->addIndex(['status'])
|
||||||
|
->addForeignKey(
|
||||||
|
'country_id',
|
||||||
|
'auth_countries',
|
||||||
|
'id',
|
||||||
|
[
|
||||||
|
'delete' => 'SET_NULL',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Brands table
|
||||||
|
* Brand/division registry linked to a company.
|
||||||
|
*/
|
||||||
|
$this->table('brands', ['id' => 'idbrand', 'signed' => false])
|
||||||
|
->addColumn('idcompany', 'integer', ['signed' => false])
|
||||||
|
->addColumn('brand_name', 'string', ['limit' => 255])
|
||||||
|
->addColumn('external_brand_code', 'string', ['limit' => 100, 'null' => true])
|
||||||
|
->addColumn('status', 'enum', [
|
||||||
|
'values' => ['active', 'inactive'],
|
||||||
|
'default' => 'active',
|
||||||
|
])
|
||||||
|
->addTimestamps()
|
||||||
|
->addIndex(['idcompany'])
|
||||||
|
->addIndex(['brand_name'])
|
||||||
|
->addIndex(['external_brand_code'])
|
||||||
|
->addIndex(['status'])
|
||||||
|
->addForeignKey(
|
||||||
|
'idcompany',
|
||||||
|
'companies',
|
||||||
|
'idcompany',
|
||||||
|
[
|
||||||
|
'delete' => 'CASCADE',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Departments table
|
||||||
|
* Department registry linked to a company and optionally to a brand.
|
||||||
|
*/
|
||||||
|
$this->table('departments', ['id' => 'iddepartment', 'signed' => false])
|
||||||
|
->addColumn('idcompany', 'integer', ['signed' => false])
|
||||||
|
->addColumn('idbrand', 'integer', ['signed' => false, 'null' => true])
|
||||||
|
->addColumn('department_name', 'string', ['limit' => 255])
|
||||||
|
->addColumn('external_department_code', 'string', ['limit' => 100, 'null' => true])
|
||||||
|
->addColumn('status', 'enum', [
|
||||||
|
'values' => ['active', 'inactive'],
|
||||||
|
'default' => 'active',
|
||||||
|
])
|
||||||
|
->addTimestamps()
|
||||||
|
->addIndex(['idcompany'])
|
||||||
|
->addIndex(['idbrand'])
|
||||||
|
->addIndex(['department_name'])
|
||||||
|
->addIndex(['external_department_code'])
|
||||||
|
->addIndex(['status'])
|
||||||
|
->addForeignKey(
|
||||||
|
'idcompany',
|
||||||
|
'companies',
|
||||||
|
'idcompany',
|
||||||
|
[
|
||||||
|
'delete' => 'CASCADE',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->addForeignKey(
|
||||||
|
'idbrand',
|
||||||
|
'brands',
|
||||||
|
'idbrand',
|
||||||
|
[
|
||||||
|
'delete' => 'SET_NULL',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->create();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Company users table
|
||||||
|
* Bridge between Vanguard users and TRFgo companies/brands/departments.
|
||||||
|
* It does not replace auth_users; it only defines data visibility and operational role.
|
||||||
|
*/
|
||||||
|
$this->table('company_users', ['id' => 'idcompanyuser', 'signed' => false])
|
||||||
|
->addColumn('iduser', 'integer', ['signed' => false])
|
||||||
|
->addColumn('idcompany', 'integer', ['signed' => false])
|
||||||
|
->addColumn('idbrand', 'integer', ['signed' => false, 'null' => true])
|
||||||
|
->addColumn('iddepartment', 'integer', ['signed' => false, 'null' => true])
|
||||||
|
->addColumn('user_scope', 'enum', [
|
||||||
|
'values' => ['company', 'brand', 'department'],
|
||||||
|
'default' => 'company',
|
||||||
|
])
|
||||||
|
->addColumn('company_role', 'enum', [
|
||||||
|
'values' => [
|
||||||
|
'owner',
|
||||||
|
'admin',
|
||||||
|
'manager',
|
||||||
|
'operator',
|
||||||
|
'viewer',
|
||||||
|
'api_user',
|
||||||
|
'lab_user',
|
||||||
|
],
|
||||||
|
'default' => 'viewer',
|
||||||
|
])
|
||||||
|
->addColumn('status', 'enum', [
|
||||||
|
'values' => ['active', 'inactive'],
|
||||||
|
'default' => 'active',
|
||||||
|
])
|
||||||
|
->addTimestamps()
|
||||||
|
->addIndex(['iduser'])
|
||||||
|
->addIndex(['idcompany'])
|
||||||
|
->addIndex(['idbrand'])
|
||||||
|
->addIndex(['iddepartment'])
|
||||||
|
->addIndex(['user_scope'])
|
||||||
|
->addIndex(['company_role'])
|
||||||
|
->addIndex(['status'])
|
||||||
|
->addIndex(
|
||||||
|
['iduser', 'idcompany', 'idbrand', 'iddepartment'],
|
||||||
|
['unique' => true, 'name' => 'uq_company_user_scope']
|
||||||
|
)
|
||||||
|
->addForeignKey(
|
||||||
|
'iduser',
|
||||||
|
'auth_users',
|
||||||
|
'id',
|
||||||
|
[
|
||||||
|
'delete' => 'CASCADE',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->addForeignKey(
|
||||||
|
'idcompany',
|
||||||
|
'companies',
|
||||||
|
'idcompany',
|
||||||
|
[
|
||||||
|
'delete' => 'CASCADE',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->addForeignKey(
|
||||||
|
'idbrand',
|
||||||
|
'brands',
|
||||||
|
'idbrand',
|
||||||
|
[
|
||||||
|
'delete' => 'SET_NULL',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->addForeignKey(
|
||||||
|
'iddepartment',
|
||||||
|
'departments',
|
||||||
|
'iddepartment',
|
||||||
|
[
|
||||||
|
'delete' => 'SET_NULL',
|
||||||
|
'update' => 'CASCADE',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
->create();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class CreateUserDashboardLayoutsTable extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
if (!$this->hasTable('user_dashboard_layouts')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `user_dashboard_layouts` (
|
||||||
|
`idlayout` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`iduser` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`page` VARCHAR(100) NOT NULL DEFAULT 'dashboard',
|
||||||
|
`layout_json` LONGTEXT NOT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idlayout`),
|
||||||
|
UNIQUE KEY `uq_user_dashboard_page` (`iduser`, `page`),
|
||||||
|
KEY `idx_dashboard_page` (`page`),
|
||||||
|
CONSTRAINT `fk_dashboard_layout_user`
|
||||||
|
FOREIGN KEY (`iduser`)
|
||||||
|
REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE CASCADE
|
||||||
|
ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
if ($this->hasTable('user_dashboard_layouts')) {
|
||||||
|
$this->table('user_dashboard_layouts')->drop()->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,427 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use Phinx\Migration\AbstractMigration;
|
||||||
|
|
||||||
|
final class CreateSampleMasterDataTables extends AbstractMigration
|
||||||
|
{
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Business partners:
|
||||||
|
* suppliers, producers, manufacturers, vendors, factories, invoice/report entities.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('business_partners')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `business_partners` (
|
||||||
|
`idpartner` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idcompany` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`partner_type` ENUM(
|
||||||
|
'producer',
|
||||||
|
'manufacturer',
|
||||||
|
'supplier',
|
||||||
|
'vendor',
|
||||||
|
'factory',
|
||||||
|
'agent',
|
||||||
|
'invoice_to',
|
||||||
|
'report_to',
|
||||||
|
'laboratory',
|
||||||
|
'other'
|
||||||
|
) NOT NULL DEFAULT 'supplier',
|
||||||
|
`partner_name` VARCHAR(255) NOT NULL,
|
||||||
|
`legal_name` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`external_code` VARCHAR(100) DEFAULT NULL,
|
||||||
|
`vat_number` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`tax_code` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`address` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`city` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`zip` VARCHAR(50) DEFAULT NULL,
|
||||||
|
`country_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`email` VARCHAR(191) DEFAULT NULL,
|
||||||
|
`phone` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`website` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`notes` TEXT DEFAULT NULL,
|
||||||
|
`status` ENUM('active','inactive','suspended') NOT NULL DEFAULT 'active',
|
||||||
|
`created_by` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idpartner`),
|
||||||
|
KEY `idx_partner_company` (`idcompany`),
|
||||||
|
KEY `idx_partner_type` (`partner_type`),
|
||||||
|
KEY `idx_partner_name` (`partner_name`),
|
||||||
|
KEY `idx_partner_external_code` (`external_code`),
|
||||||
|
KEY `idx_partner_status` (`status`),
|
||||||
|
KEY `idx_partner_country` (`country_id`),
|
||||||
|
KEY `idx_partner_created_by` (`created_by`),
|
||||||
|
CONSTRAINT `fk_partner_company`
|
||||||
|
FOREIGN KEY (`idcompany`) REFERENCES `companies` (`idcompany`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_partner_country`
|
||||||
|
FOREIGN KEY (`country_id`) REFERENCES `auth_countries` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_partner_created_by`
|
||||||
|
FOREIGN KEY (`created_by`) REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Contacts linked to business partners.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('business_partner_contacts')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `business_partner_contacts` (
|
||||||
|
`idcontact` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idpartner` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`contact_name` VARCHAR(255) NOT NULL,
|
||||||
|
`role` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`email` VARCHAR(191) DEFAULT NULL,
|
||||||
|
`phone` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`mobile` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`is_primary` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
|
`notes` TEXT DEFAULT NULL,
|
||||||
|
`status` ENUM('active','inactive') NOT NULL DEFAULT 'active',
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idcontact`),
|
||||||
|
KEY `idx_contact_partner` (`idpartner`),
|
||||||
|
KEY `idx_contact_email` (`email`),
|
||||||
|
KEY `idx_contact_primary` (`is_primary`),
|
||||||
|
CONSTRAINT `fk_contact_partner`
|
||||||
|
FOREIGN KEY (`idpartner`) REFERENCES `business_partners` (`idpartner`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Samples / products master data.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('samples')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `samples` (
|
||||||
|
`idsample` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idcompany` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`idbrand` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`iddepartment` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`idproducer` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`idsupplier` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`sample_code` VARCHAR(120) NOT NULL,
|
||||||
|
`external_sample_id` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`article_no` VARCHAR(150) DEFAULT NULL,
|
||||||
|
`po_no` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`season` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`style_no` VARCHAR(150) DEFAULT NULL,
|
||||||
|
`style_name` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`model` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`sample_description` VARCHAR(255) NOT NULL,
|
||||||
|
`product_category` VARCHAR(150) DEFAULT NULL,
|
||||||
|
`product_type` VARCHAR(150) DEFAULT NULL,
|
||||||
|
`color` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`size` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`gender` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`age_group` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`fiber_content` TEXT DEFAULT NULL,
|
||||||
|
`material_description` TEXT DEFAULT NULL,
|
||||||
|
`claimed_weight` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`product_standard` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`production_stage` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`country_of_origin` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`status` ENUM(
|
||||||
|
'draft',
|
||||||
|
'active',
|
||||||
|
'archived',
|
||||||
|
'submitted',
|
||||||
|
'under_testing',
|
||||||
|
'completed',
|
||||||
|
'cancelled'
|
||||||
|
) NOT NULL DEFAULT 'draft',
|
||||||
|
`source` ENUM('manual','xls_import','json_import','api','smarttrf') NOT NULL DEFAULT 'manual',
|
||||||
|
`raw_json` LONGTEXT DEFAULT NULL,
|
||||||
|
`created_by` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idsample`),
|
||||||
|
UNIQUE KEY `uq_sample_company_code` (`idcompany`, `sample_code`),
|
||||||
|
KEY `idx_sample_company` (`idcompany`),
|
||||||
|
KEY `idx_sample_brand` (`idbrand`),
|
||||||
|
KEY `idx_sample_department` (`iddepartment`),
|
||||||
|
KEY `idx_sample_producer` (`idproducer`),
|
||||||
|
KEY `idx_sample_supplier` (`idsupplier`),
|
||||||
|
KEY `idx_sample_article` (`article_no`),
|
||||||
|
KEY `idx_sample_external` (`external_sample_id`),
|
||||||
|
KEY `idx_sample_status` (`status`),
|
||||||
|
KEY `idx_sample_created_by` (`created_by`),
|
||||||
|
CONSTRAINT `fk_sample_company`
|
||||||
|
FOREIGN KEY (`idcompany`) REFERENCES `companies` (`idcompany`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_brand`
|
||||||
|
FOREIGN KEY (`idbrand`) REFERENCES `brands` (`idbrand`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_department`
|
||||||
|
FOREIGN KEY (`iddepartment`) REFERENCES `departments` (`iddepartment`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_producer`
|
||||||
|
FOREIGN KEY (`idproducer`) REFERENCES `business_partners` (`idpartner`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_supplier`
|
||||||
|
FOREIGN KEY (`idsupplier`) REFERENCES `business_partners` (`idpartner`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_origin_country`
|
||||||
|
FOREIGN KEY (`country_of_origin`) REFERENCES `auth_countries` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_created_by`
|
||||||
|
FOREIGN KEY (`created_by`) REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample status history.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('sample_status_history')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `sample_status_history` (
|
||||||
|
`idhistory` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idsample` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`old_status` VARCHAR(80) DEFAULT NULL,
|
||||||
|
`new_status` VARCHAR(80) NOT NULL,
|
||||||
|
`note` TEXT DEFAULT NULL,
|
||||||
|
`changed_by` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idhistory`),
|
||||||
|
KEY `idx_history_sample` (`idsample`),
|
||||||
|
KEY `idx_history_changed_by` (`changed_by`),
|
||||||
|
CONSTRAINT `fk_sample_history_sample`
|
||||||
|
FOREIGN KEY (`idsample`) REFERENCES `samples` (`idsample`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_history_user`
|
||||||
|
FOREIGN KEY (`changed_by`) REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample photos.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('sample_photos')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `sample_photos` (
|
||||||
|
`idsamplephoto` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idsample` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`photo_type` ENUM('main','product','label','packaging','warning','detail','other') NOT NULL DEFAULT 'product',
|
||||||
|
`filename` VARCHAR(255) NOT NULL,
|
||||||
|
`original_filename` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`description` TEXT DEFAULT NULL,
|
||||||
|
`is_main` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
|
`sort_order` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
`uploaded_by` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idsamplephoto`),
|
||||||
|
KEY `idx_sample_photo_sample` (`idsample`),
|
||||||
|
KEY `idx_sample_photo_type` (`photo_type`),
|
||||||
|
KEY `idx_sample_photo_uploaded_by` (`uploaded_by`),
|
||||||
|
CONSTRAINT `fk_sample_photo_sample`
|
||||||
|
FOREIGN KEY (`idsample`) REFERENCES `samples` (`idsample`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_photo_user`
|
||||||
|
FOREIGN KEY (`uploaded_by`) REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample parts / BOM.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('sample_parts')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `sample_parts` (
|
||||||
|
`idpart` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idsample` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`parent_idpart` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`part_code` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`part_name` VARCHAR(255) NOT NULL,
|
||||||
|
`part_description` TEXT DEFAULT NULL,
|
||||||
|
`material` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`color` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`quantity` DECIMAL(12,4) DEFAULT NULL,
|
||||||
|
`unit` VARCHAR(50) DEFAULT NULL,
|
||||||
|
`supplier_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`producer_id` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`position` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`risk_level` ENUM('low','medium','high','critical','unknown') NOT NULL DEFAULT 'unknown',
|
||||||
|
`notes` TEXT DEFAULT NULL,
|
||||||
|
`sort_order` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idpart`),
|
||||||
|
KEY `idx_part_sample` (`idsample`),
|
||||||
|
KEY `idx_part_parent` (`parent_idpart`),
|
||||||
|
KEY `idx_part_supplier` (`supplier_id`),
|
||||||
|
KEY `idx_part_producer` (`producer_id`),
|
||||||
|
KEY `idx_part_risk` (`risk_level`),
|
||||||
|
CONSTRAINT `fk_part_sample`
|
||||||
|
FOREIGN KEY (`idsample`) REFERENCES `samples` (`idsample`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_part_parent`
|
||||||
|
FOREIGN KEY (`parent_idpart`) REFERENCES `sample_parts` (`idpart`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_part_supplier`
|
||||||
|
FOREIGN KEY (`supplier_id`) REFERENCES `business_partners` (`idpartner`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_part_producer`
|
||||||
|
FOREIGN KEY (`producer_id`) REFERENCES `business_partners` (`idpartner`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Sample part photos.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('sample_part_photos')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `sample_part_photos` (
|
||||||
|
`idpartphoto` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idpart` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`filename` VARCHAR(255) NOT NULL,
|
||||||
|
`original_filename` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`description` TEXT DEFAULT NULL,
|
||||||
|
`is_main` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
|
`sort_order` INT(11) NOT NULL DEFAULT 0,
|
||||||
|
`uploaded_by` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idpartphoto`),
|
||||||
|
KEY `idx_part_photo_part` (`idpart`),
|
||||||
|
KEY `idx_part_photo_uploaded_by` (`uploaded_by`),
|
||||||
|
CONSTRAINT `fk_part_photo_part`
|
||||||
|
FOREIGN KEY (`idpart`) REFERENCES `sample_parts` (`idpart`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_part_photo_user`
|
||||||
|
FOREIGN KEY (`uploaded_by`) REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Generic documents.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('documents')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `documents` (
|
||||||
|
`iddocument` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idcompany` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`document_type` ENUM(
|
||||||
|
'technical_sheet',
|
||||||
|
'declaration',
|
||||||
|
'bom',
|
||||||
|
'photo',
|
||||||
|
'certificate',
|
||||||
|
'test_report',
|
||||||
|
'supplier_document',
|
||||||
|
'invoice',
|
||||||
|
'manual',
|
||||||
|
'other'
|
||||||
|
) NOT NULL DEFAULT 'other',
|
||||||
|
`title` VARCHAR(255) NOT NULL,
|
||||||
|
`filename` VARCHAR(255) NOT NULL,
|
||||||
|
`original_filename` VARCHAR(255) DEFAULT NULL,
|
||||||
|
`mime_type` VARCHAR(120) DEFAULT NULL,
|
||||||
|
`file_size` BIGINT(20) UNSIGNED DEFAULT NULL,
|
||||||
|
`expiry_date` DATE DEFAULT NULL,
|
||||||
|
`status` ENUM('active','expired','archived') NOT NULL DEFAULT 'active',
|
||||||
|
`notes` TEXT DEFAULT NULL,
|
||||||
|
`uploaded_by` INT(10) UNSIGNED DEFAULT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
`updated_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`iddocument`),
|
||||||
|
KEY `idx_document_company` (`idcompany`),
|
||||||
|
KEY `idx_document_type` (`document_type`),
|
||||||
|
KEY `idx_document_status` (`status`),
|
||||||
|
KEY `idx_document_expiry` (`expiry_date`),
|
||||||
|
KEY `idx_document_uploaded_by` (`uploaded_by`),
|
||||||
|
CONSTRAINT `fk_document_company`
|
||||||
|
FOREIGN KEY (`idcompany`) REFERENCES `companies` (`idcompany`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_document_uploaded_by`
|
||||||
|
FOREIGN KEY (`uploaded_by`) REFERENCES `auth_users` (`id`)
|
||||||
|
ON DELETE SET NULL ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Documents linked to samples.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('sample_documents')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `sample_documents` (
|
||||||
|
`idsampledocument` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idsample` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`iddocument` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idsampledocument`),
|
||||||
|
UNIQUE KEY `uq_sample_document` (`idsample`, `iddocument`),
|
||||||
|
KEY `idx_sample_document_document` (`iddocument`),
|
||||||
|
CONSTRAINT `fk_sample_document_sample`
|
||||||
|
FOREIGN KEY (`idsample`) REFERENCES `samples` (`idsample`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_sample_document_document`
|
||||||
|
FOREIGN KEY (`iddocument`) REFERENCES `documents` (`iddocument`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Documents linked to sample parts.
|
||||||
|
*/
|
||||||
|
if (!$this->hasTable('sample_part_documents')) {
|
||||||
|
$this->execute("
|
||||||
|
CREATE TABLE `sample_part_documents` (
|
||||||
|
`idpartdocument` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||||
|
`idpart` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`iddocument` INT(10) UNSIGNED NOT NULL,
|
||||||
|
`created_at` TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`idpartdocument`),
|
||||||
|
UNIQUE KEY `uq_part_document` (`idpart`, `iddocument`),
|
||||||
|
KEY `idx_part_document_document` (`iddocument`),
|
||||||
|
CONSTRAINT `fk_part_document_part`
|
||||||
|
FOREIGN KEY (`idpart`) REFERENCES `sample_parts` (`idpart`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE,
|
||||||
|
CONSTRAINT `fk_part_document_document`
|
||||||
|
FOREIGN KEY (`iddocument`) REFERENCES `documents` (`iddocument`)
|
||||||
|
ON DELETE CASCADE ON UPDATE CASCADE
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||||
|
");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
$tables = [
|
||||||
|
'sample_part_documents',
|
||||||
|
'sample_documents',
|
||||||
|
'documents',
|
||||||
|
'sample_part_photos',
|
||||||
|
'sample_parts',
|
||||||
|
'sample_photos',
|
||||||
|
'sample_status_history',
|
||||||
|
'samples',
|
||||||
|
'business_partner_contacts',
|
||||||
|
'business_partners',
|
||||||
|
];
|
||||||
|
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
if ($this->hasTable($table)) {
|
||||||
|
$this->table($table)->drop()->save();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
require_once __DIR__ . '/vendor/autoload.php';
|
||||||
|
|
||||||
|
if (file_exists(__DIR__ . '/.env')) {
|
||||||
|
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||||
|
$dotenv->safeLoad();
|
||||||
|
}
|
||||||
|
|
||||||
|
return [
|
||||||
|
'paths' => [
|
||||||
|
'migrations' => __DIR__ . '/db/migrations',
|
||||||
|
'seeds' => __DIR__ . '/db/seeds',
|
||||||
|
],
|
||||||
|
|
||||||
|
'environments' => [
|
||||||
|
'default_migration_table' => 'phinxlog',
|
||||||
|
'default_environment' => 'development',
|
||||||
|
|
||||||
|
'development' => [
|
||||||
|
'adapter' => 'mysql',
|
||||||
|
'host' => $_ENV['DB_HOST'] ?? '127.0.0.1',
|
||||||
|
'name' => $_ENV['DB_DATABASE'] ?? 'trfgo',
|
||||||
|
'user' => $_ENV['DB_USERNAME'] ?? 'root',
|
||||||
|
'pass' => $_ENV['DB_PASSWORD'] ?? '',
|
||||||
|
'port' => $_ENV['DB_PORT'] ?? '3306',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
],
|
||||||
|
|
||||||
|
'production' => [
|
||||||
|
'adapter' => 'mysql',
|
||||||
|
'host' => $_ENV['DB_HOST'] ?? '127.0.0.1',
|
||||||
|
'name' => $_ENV['DB_DATABASE'] ?? 'trfgo',
|
||||||
|
'user' => $_ENV['DB_USERNAME'] ?? 'root',
|
||||||
|
'pass' => $_ENV['DB_PASSWORD'] ?? '',
|
||||||
|
'port' => $_ENV['DB_PORT'] ?? '3306',
|
||||||
|
'charset' => 'utf8mb4',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
|
||||||
|
'version_order' => 'creation',
|
||||||
|
];
|
||||||
@@ -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,854 +0,0 @@
|
|||||||
(function () {
|
|
||||||
"use strict";
|
|
||||||
let analysisMatriciMap = {};
|
|
||||||
let analysisLoadedCache = {};
|
|
||||||
let analysisAssignedState = {};
|
|
||||||
let analysisSelectedState = {};
|
|
||||||
|
|
||||||
function loadAnalysisMatrixNames() {
|
|
||||||
return $.ajax({
|
|
||||||
url: "get_matrici_db.php",
|
|
||||||
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) {
|
|
||||||
const recordKey = item.analysis_recordkey || "";
|
|
||||||
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)}"
|
|
||||||
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) {
|
|
||||||
const key = String(partId);
|
|
||||||
if (!Array.isArray(analysisAssignedState[key])) {
|
|
||||||
analysisAssignedState[key] = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
const exists = analysisAssignedState[key].some(function (item) {
|
|
||||||
return item.analysis_recordkey === payload.analysis_recordkey;
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!exists) {
|
|
||||||
analysisAssignedState[key].push({
|
|
||||||
id: 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) {
|
|
||||||
const key = String(partId);
|
|
||||||
if (!Array.isArray(analysisAssignedState[key])) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
analysisAssignedState[key] = analysisAssignedState[key].filter(
|
|
||||||
function (item) {
|
|
||||||
return item.analysis_recordkey !== recordKey;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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 () {
|
|
||||||
addAnalysisToLocalState(
|
|
||||||
partId,
|
|
||||||
payload,
|
|
||||||
iddatadb,
|
|
||||||
idmatrice !== "NO_MATRIX" ? idmatrice : 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, callback) {
|
|
||||||
$.ajax({
|
|
||||||
url: "delete_part_analysis.php",
|
|
||||||
method: "POST",
|
|
||||||
dataType: "json",
|
|
||||||
data: {
|
|
||||||
part_id: partId,
|
|
||||||
analysis_recordkey: recordKey,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
.done(function () {
|
|
||||||
removeAnalysisFromLocalState(partId, recordKey);
|
|
||||||
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) {
|
|
||||||
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");
|
|
||||||
|
|
||||||
deleteAnalysisAssociation(partId, recordKey, 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;
|
|
||||||
const alreadySelected = !!analysisSelectedState[recordKey];
|
|
||||||
|
|
||||||
if (alreadySelected) {
|
|
||||||
selectedPartIds.forEach(function (partId) {
|
|
||||||
deleteAnalysisAssociation(partId, recordKey);
|
|
||||||
});
|
|
||||||
|
|
||||||
delete analysisSelectedState[recordKey];
|
|
||||||
syncSelectedAnalysisRows();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
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,58 +0,0 @@
|
|||||||
<?php
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// URL dell'API e credenziali
|
|
||||||
$api_url = 'https://93.43.5.102/limsapi/api/authentication/authenticate';
|
|
||||||
$credentials = [
|
|
||||||
'Username' => 'WebApiUser',
|
|
||||||
'Password' => 'webapiuser01'
|
|
||||||
];
|
|
||||||
|
|
||||||
// Inizializza cURL
|
|
||||||
$ch = curl_init($api_url);
|
|
||||||
|
|
||||||
// Configura le opzioni di cURL
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($credentials));
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Content-Type: application/json',
|
|
||||||
'Accept: application/json'
|
|
||||||
]);
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Solo per test
|
|
||||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Solo per test
|
|
||||||
curl_setopt($ch, CURLOPT_VERBOSE, true);
|
|
||||||
$log = fopen('curl_auth_debug.log', 'w');
|
|
||||||
curl_setopt($ch, CURLOPT_STDERR, $log);
|
|
||||||
|
|
||||||
// Esegui la richiesta
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$curl_error = curl_error($ch);
|
|
||||||
fclose($log);
|
|
||||||
|
|
||||||
// Verifica errori
|
|
||||||
if ($response === false || $http_code != 200) {
|
|
||||||
http_response_code($http_code ? $http_code : 500);
|
|
||||||
echo json_encode([
|
|
||||||
'error' => 'Errore nella richiesta API',
|
|
||||||
'http_code' => $http_code,
|
|
||||||
'curl_error' => $curl_error,
|
|
||||||
'response' => substr($response, 0, 1000)
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
$decoded = json_decode($response);
|
|
||||||
if (json_last_error() === JSON_ERROR_NONE) {
|
|
||||||
http_response_code($http_code);
|
|
||||||
echo $response;
|
|
||||||
} else {
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode([
|
|
||||||
'error' => 'Risposta non JSON valida',
|
|
||||||
'http_code' => $http_code,
|
|
||||||
'response' => substr($response, 0, 1000)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
curl_close($ch);
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -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'
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,133 +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'] ?? [];
|
|
||||||
|
|
||||||
$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
|
|
||||||
");
|
|
||||||
|
|
||||||
$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;
|
|
||||||
|
|
||||||
// 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' => $part['note'] ?? 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,
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$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,
|
|
||||||
'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'));
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,211 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Questo file può essere vuoto o contenere logica PHP aggiuntiva se necessario
|
|
||||||
?>
|
|
||||||
<!DOCTYPE html>
|
|
||||||
<html lang="it">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="UTF-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
||||||
<title>Autenticazione VisualLims</title>
|
|
||||||
<!-- Includi Select2 CSS -->
|
|
||||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
|
||||||
<style>
|
|
||||||
body {
|
|
||||||
font-family: Arial, sans-serif;
|
|
||||||
max-width: 600px;
|
|
||||||
margin: 20px auto;
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#authButton {
|
|
||||||
padding: 10px 20px;
|
|
||||||
background-color: #007bff;
|
|
||||||
color: white;
|
|
||||||
border: none;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#authButton:hover {
|
|
||||||
background-color: #0056b3;
|
|
||||||
}
|
|
||||||
|
|
||||||
#result {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 10px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
#schemiResult {
|
|
||||||
margin-top: 20px;
|
|
||||||
padding: 10px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
word-wrap: break-word;
|
|
||||||
}
|
|
||||||
|
|
||||||
.select2-container {
|
|
||||||
width: 100% !important;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>Autenticazione VisualLims</h1>
|
|
||||||
<button id="authButton">Autentica</button>
|
|
||||||
<div id="result"></div>
|
|
||||||
|
|
||||||
<!-- Tendina per i clienti -->
|
|
||||||
<h3>Seleziona un cliente:</h3>
|
|
||||||
<select id="clientiSelect" style="width: 100%;">
|
|
||||||
<option value="">Seleziona un cliente...</option>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<!-- Area per mostrare gli schemi -->
|
|
||||||
<div id="schemiResult"></div>
|
|
||||||
|
|
||||||
<!-- Includi jQuery (necessario per Select2) -->
|
|
||||||
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
|
|
||||||
<!-- Includi Select2 JS -->
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Inizializza Select2 sulla tendina
|
|
||||||
$(document).ready(function() {
|
|
||||||
$('#clientiSelect').select2({
|
|
||||||
placeholder: "Cerca un cliente...",
|
|
||||||
allowClear: true
|
|
||||||
});
|
|
||||||
|
|
||||||
// Carica i clienti al caricamento della pagina
|
|
||||||
loadClienti();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Autenticazione
|
|
||||||
document.getElementById('authButton').addEventListener('click', async () => {
|
|
||||||
const resultDiv = document.getElementById('result');
|
|
||||||
resultDiv.textContent = 'Autenticazione in corso...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('auth_proxy.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(`Errore HTTP! Stato: ${response.status}, Dettagli: ${data.error || 'Nessun dettaglio disponibile'}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof data === 'string' && data.length > 0) {
|
|
||||||
resultDiv.textContent = `Token: ${data}`;
|
|
||||||
} else if (data && data.token) {
|
|
||||||
resultDiv.textContent = `Token: ${data.token}`;
|
|
||||||
} else {
|
|
||||||
resultDiv.textContent = `Autenticazione fallita: Nessun token ricevuto. Dettagli: ${JSON.stringify(data)}`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
resultDiv.textContent = `Errore: ${error.message}`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Funzione per caricare i clienti nella tendina
|
|
||||||
async function loadClienti() {
|
|
||||||
const resultDiv = document.getElementById('result');
|
|
||||||
resultDiv.textContent = 'Caricamento clienti...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('get_clienti.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({})
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.error || `Errore HTTP: ${response.status}, Dettagli: ${JSON.stringify(data)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.value && Array.isArray(data.value)) {
|
|
||||||
const select = document.getElementById('clientiSelect');
|
|
||||||
data.value.forEach(c => {
|
|
||||||
const nome = c.Nominativo || 'Nome non disponibile';
|
|
||||||
const id = c.IdCliente || 'ID non disponibile';
|
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
|
||||||
select.add(option);
|
|
||||||
});
|
|
||||||
resultDiv.textContent = 'Clienti caricati con successo.';
|
|
||||||
} else {
|
|
||||||
resultDiv.textContent = 'Nessun cliente trovato o formato dati non valido.';
|
|
||||||
console.log('Risposta API:', data);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
resultDiv.textContent = 'Errore: ' + err.message;
|
|
||||||
console.error('Dettagli errore:', err);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evento per gestire la selezione di un cliente e recuperare gli schemi
|
|
||||||
$('#clientiSelect').on('select2:select', async function(e) {
|
|
||||||
const clienteId = e.target.value; // Oppure: $(this).val()
|
|
||||||
const schemiDiv = document.getElementById('schemiResult'); // Correzione del nome variabile
|
|
||||||
|
|
||||||
// Log per debug
|
|
||||||
console.log('Cliente selezionato:', clienteId);
|
|
||||||
|
|
||||||
if (!clienteId) {
|
|
||||||
schemiDiv.textContent = '';
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
schemiDiv.textContent = 'Caricamento schemi...';
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch('get_schemi.php', {
|
|
||||||
method: 'POST',
|
|
||||||
headers: {
|
|
||||||
'Content-Type': 'application/json'
|
|
||||||
},
|
|
||||||
body: JSON.stringify({
|
|
||||||
clienteId
|
|
||||||
})
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.error || `Errore HTTP: ${response.status}, Dettagli: ${JSON.stringify(data)}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.SchemiAbilitati && Array.isArray(data.SchemiAbilitati)) {
|
|
||||||
let html = '<h3>Schemi Abilitati:</h3><ul>';
|
|
||||||
data.SchemiAbilitati.forEach(s => {
|
|
||||||
const nomeSchema = s.NomeSchema || s.Descrizione || 'Schema non specificato';
|
|
||||||
html += `<li>${nomeSchema}</li>`;
|
|
||||||
});
|
|
||||||
html += '</ul>';
|
|
||||||
schemiDiv.innerHTML = html;
|
|
||||||
} else {
|
|
||||||
schemiDiv.textContent = 'Nessuno schema trovato per questo cliente.';
|
|
||||||
console.log('Risposta Schemi:', data);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
schemiDiv.textContent = 'Errore: ' + err.message;
|
|
||||||
console.error('Dettagli errore:', err);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Gestisci la deselezione (opzionale)
|
|
||||||
$('#clientiSelect').on('select2:unselect', function(e) {
|
|
||||||
const schemiDiv = document.getElementById('schemiResult'); // Correzione del nome variabile
|
|
||||||
schemiDiv.textContent = '';
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,79 +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 (no UI selection)
|
|
||||||
* is_manual always 1
|
|
||||||
* is_visible_import default 1
|
|
||||||
*/
|
|
||||||
$fixedFields = [
|
|
||||||
// fixed_field_key, data_type
|
|
||||||
['ClienteResponsabile', 'INT'],
|
|
||||||
['ClienteFornitore', 'INT'],
|
|
||||||
['ClienteAnalisi', 'INT'],
|
|
||||||
['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
File diff suppressed because it is too large
Load Diff
@@ -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,29 +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,48 +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'] ?? '');
|
|
||||||
|
|
||||||
if ($partId <= 0 || $analysisRecordkey === '') {
|
|
||||||
http_response_code(400);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Missing required data']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("
|
|
||||||
DELETE FROM identification_parts_analyses
|
|
||||||
WHERE part_id = :part_id
|
|
||||||
AND analysis_recordkey = :analysis_recordkey
|
|
||||||
");
|
|
||||||
$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,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
// delete_photo.php
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['photo_id'])) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Richiesta non valida']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$photoId = intval($_POST['photo_id']);
|
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
// Recupera il percorso del file
|
|
||||||
$stmt = $pdo->prepare("SELECT file_path FROM datadb_photos WHERE id = ?");
|
|
||||||
$stmt->execute([$photoId]);
|
|
||||||
$photo = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$photo) {
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Foto non trovata']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elimina il file dal server
|
|
||||||
$filePath = '../photostrf/' . $photo['file_path'];
|
|
||||||
if (file_exists($filePath)) {
|
|
||||||
unlink($filePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Elimina il record dal database
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM datadb_photos WHERE id = ?");
|
|
||||||
$stmt->execute([$photoId]);
|
|
||||||
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Foto eliminata con successo']);
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Enable errors for debugging
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('log_errors', 1);
|
|
||||||
ini_set('error_log', __DIR__ . '/delete_record_debug.log');
|
|
||||||
|
|
||||||
// Log iniziale
|
|
||||||
error_log("Inizio cancellazione record alle " . date('Y-m-d H:i:s'));
|
|
||||||
|
|
||||||
// Includi il file di configurazione del database
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
// Ricevi l'ID dalla richiesta POST
|
|
||||||
$input = json_decode(file_get_contents('php://input'), true);
|
|
||||||
$iddatadb = isset($input['id']) ? intval($input['id']) : 0;
|
|
||||||
|
|
||||||
if ($iddatadb <= 0) {
|
|
||||||
http_response_code(400);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'ID non valido']);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Connessione al database
|
|
||||||
try {
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore di connessione al database: ' . $e->getMessage()]);
|
|
||||||
error_log("Errore di connessione al database: " . $e->getMessage());
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inizia una transazione
|
|
||||||
$pdo->beginTransaction();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Elimina i dettagli associati dal tavolo import_data_details
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM import_data_details WHERE id = ?");
|
|
||||||
$stmt->execute([$iddatadb]);
|
|
||||||
|
|
||||||
// Elimina il record principale dal tavolo datadb
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM datadb WHERE iddatadb = ?");
|
|
||||||
$stmt->execute([$iddatadb]);
|
|
||||||
|
|
||||||
// Verifica se il record è stato eliminato
|
|
||||||
if ($stmt->rowCount() > 0) {
|
|
||||||
$pdo->commit();
|
|
||||||
echo json_encode(['success' => true, 'message' => 'Record eliminato con successo']);
|
|
||||||
error_log("Record con iddatadb=$iddatadb eliminato con successo");
|
|
||||||
} else {
|
|
||||||
$pdo->rollBack();
|
|
||||||
http_response_code(404);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Record non trovato']);
|
|
||||||
error_log("Record con iddatadb=$iddatadb non trovato");
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$pdo->rollBack();
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode(['success' => false, 'message' => 'Errore durante la cancellazione: ' . $e->getMessage()]);
|
|
||||||
error_log("Errore durante la cancellazione del record con iddatadb=$iddatadb: " . $e->getMessage());
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Abilita la gestione degli errori
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// Includi la connessione al database
|
|
||||||
require_once 'class/db-functions.php';
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Controlla se è stato passato un ID valido
|
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
||||||
throw new Exception('Invalid ID');
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = intval($_GET['id']); // Sanifica l'ID
|
|
||||||
|
|
||||||
// Connessione al database
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
if (!$pdo) {
|
|
||||||
throw new Exception('Database connection failed');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica se l'ID esiste
|
|
||||||
$stmtCheck = $pdo->prepare("SELECT id FROM excel_templates WHERE id = ?");
|
|
||||||
$stmtCheck->execute([$id]);
|
|
||||||
if ($stmtCheck->rowCount() === 0) {
|
|
||||||
throw new Exception('Template not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Esegui l'eliminazione
|
|
||||||
$stmt = $pdo->prepare("DELETE FROM excel_templates WHERE id = ?");
|
|
||||||
$stmt->execute([$id]);
|
|
||||||
|
|
||||||
if ($stmt->rowCount() > 0) {
|
|
||||||
$message = "Template deleted successfully!";
|
|
||||||
$status = "success";
|
|
||||||
} else {
|
|
||||||
throw new Exception('Failed to delete template');
|
|
||||||
}
|
|
||||||
} catch (Exception $e) {
|
|
||||||
$message = $e->getMessage();
|
|
||||||
$status = "error";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Reindirizza con un messaggio
|
|
||||||
header("Location: templates_dashboard.php?status=$status&message=" . urlencode($message));
|
|
||||||
exit;
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -1,766 +0,0 @@
|
|||||||
<?php include('include/headscript.php');
|
|
||||||
|
|
||||||
// Check if a valid ID was provided
|
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
||||||
header("Location: templates_dashboard.php?status=error&message=" . urlencode("Invalid ID"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = intval($_GET['id']);
|
|
||||||
|
|
||||||
// Retrieve template from database
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
|
||||||
$stmt->execute([$id]);
|
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$template) {
|
|
||||||
header("Location: templates_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Retrieve all routines
|
|
||||||
$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);
|
|
||||||
|
|
||||||
$buttonBgPalette = [
|
|
||||||
'#0d6efd' => 'Blue',
|
|
||||||
'#6610f2' => 'Indigo',
|
|
||||||
'#6f42c1' => 'Purple',
|
|
||||||
'#d63384' => 'Pink',
|
|
||||||
'#dc3545' => 'Red',
|
|
||||||
'#fd7e14' => 'Orange',
|
|
||||||
'#ffc107' => 'Yellow',
|
|
||||||
'#198754' => 'Green',
|
|
||||||
'#20c997' => 'Teal',
|
|
||||||
'#0dcaf0' => 'Cyan',
|
|
||||||
'#212529' => 'Dark',
|
|
||||||
'#6c757d' => 'Gray',
|
|
||||||
'#495057' => 'Slate',
|
|
||||||
'#795548' => 'Brown',
|
|
||||||
'#2f5d50' => 'Sage Green',
|
|
||||||
];
|
|
||||||
|
|
||||||
$buttonTextPalette = [
|
|
||||||
'#ffffff' => 'White',
|
|
||||||
'#6c757d' => 'Gray',
|
|
||||||
'#000000' => 'Black',
|
|
||||||
];
|
|
||||||
|
|
||||||
$currentButtonBgColor = strtolower($template['button_bg_color'] ?? '#007bff');
|
|
||||||
$currentButtonTextColor = strtolower($template['button_text_color'] ?? '#ffffff');
|
|
||||||
|
|
||||||
if (!array_key_exists($currentButtonBgColor, array_change_key_case($buttonBgPalette, CASE_LOWER))) {
|
|
||||||
$currentButtonBgColor = '#0d6efd';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!array_key_exists($currentButtonTextColor, array_change_key_case($buttonTextPalette, CASE_LOWER))) {
|
|
||||||
$currentButtonTextColor = '#ffffff';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
||||||
<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'); ?>
|
|
||||||
<style>
|
|
||||||
.color-palette {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
gap: 10px;
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-option {
|
|
||||||
position: relative;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-option input {
|
|
||||||
position: absolute;
|
|
||||||
opacity: 0;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-swatch {
|
|
||||||
width: 42px;
|
|
||||||
height: 42px;
|
|
||||||
border-radius: 10px;
|
|
||||||
border: 2px solid #d6dbe0;
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.12);
|
|
||||||
transition: all 0.18s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-option input:checked+.color-swatch {
|
|
||||||
border: 3px solid #111827;
|
|
||||||
transform: scale(1.08);
|
|
||||||
box-shadow: 0 0 0 4px rgba(13, 110, 253, 0.18);
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-swatch .check-mark {
|
|
||||||
display: none;
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 700;
|
|
||||||
color: #ffffff;
|
|
||||||
text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-option input:checked+.color-swatch .check-mark {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
.color-name {
|
|
||||||
display: block;
|
|
||||||
font-size: 11px;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 4px;
|
|
||||||
color: #555;
|
|
||||||
max-width: 55px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<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>Edit Template <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<?php include('include/navbar.php'); ?>
|
|
||||||
<?php include('include/topbar.php'); ?>
|
|
||||||
|
|
||||||
<div class="page-wrapper">
|
|
||||||
<div class="page-content">
|
|
||||||
|
|
||||||
<div class="card mb-4">
|
|
||||||
<div class="card-header">
|
|
||||||
<h5 class="mb-0">Update Template</h5>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<p class="mb-2">Edit the following form in order to update the selected import 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 and Column Header only for XLS templates</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card radius-10">
|
|
||||||
<div class="card-header">
|
|
||||||
<div class="d-flex align-items-center">
|
|
||||||
<div>
|
|
||||||
<h6 class="mb-0">Edit Template: <?php echo htmlspecialchars($template['name']); ?></h6>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body">
|
|
||||||
<div class="col-12">
|
|
||||||
<form id="editTemplateForm" method="POST">
|
|
||||||
<input type="hidden" name="id" value="<?php echo (int)$template['id']; ?>">
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label"><?= htmlspecialchars($templatename, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
|
||||||
<input type="text" name="name" class="form-control" value="<?php echo htmlspecialchars($template['name'] ?? ''); ?>" 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" <?php echo (($template['source_type'] ?? 'XLS') === 'XLS') ? 'selected' : ''; ?>>XLS</option>
|
|
||||||
<option value="API" <?php echo (($template['source_type'] ?? 'XLS') === 'API') ? 'selected' : ''; ?>>API</option>
|
|
||||||
<option value="JSON" <?php echo (($template['source_type'] ?? 'XLS') === 'JSON') ? 'selected' : ''; ?>>JSON</option>
|
|
||||||
<option value="PDF" <?php echo (($template['source_type'] ?? 'XLS') === 'PDF') ? 'selected' : ''; ?>>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="<?php echo htmlspecialchars($template['header_row'] ?? ''); ?>">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3" id="startColumnWrapper">
|
|
||||||
<label class="form-label"><?= htmlspecialchars($columnheader, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
|
||||||
<input type="text" name="start_column" id="startColumn" class="form-control" value="<?php echo htmlspecialchars($template['start_column'] ?? ''); ?>">
|
|
||||||
</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="<?php echo htmlspecialchars($template['xls_sheet_index'] ?? 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 ((int)($template['api_config_id'] ?? 0) === (int)$apiConfig['id']) ? 'selected' : ''; ?>>
|
|
||||||
<?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>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label"><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></label>
|
|
||||||
<textarea name="description" class="form-control"><?php echo htmlspecialchars($template['description'] ?? ''); ?></textarea>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label"><?= htmlspecialchars($desttable, ENT_QUOTES, 'UTF-8'); ?> *</label>
|
|
||||||
<input type="text" name="target_table" class="form-control" value="<?php echo htmlspecialchars($template['target_table'] ?? 'datadb'); ?>" readonly required>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Button Size</label>
|
|
||||||
<select name="button_size" class="form-control">
|
|
||||||
<option value="small" <?php echo ($template['button_size'] ?? 'medium') === 'small' ? 'selected' : ''; ?>>Small</option>
|
|
||||||
<option value="medium" <?php echo ($template['button_size'] ?? 'medium') === 'medium' ? 'selected' : ''; ?>>Medium</option>
|
|
||||||
<option value="large" <?php echo ($template['button_size'] ?? 'medium') === 'large' ? 'selected' : ''; ?>>Large</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Button Background Color</label>
|
|
||||||
|
|
||||||
<div class="color-palette">
|
|
||||||
<?php foreach ($buttonBgPalette as $colorValue => $colorLabel): ?>
|
|
||||||
<?php $isChecked = strtolower($colorValue) === $currentButtonBgColor; ?>
|
|
||||||
|
|
||||||
<label class="color-option" title="<?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="button_bg_color"
|
|
||||||
value="<?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>"
|
|
||||||
<?= $isChecked ? 'checked' : ''; ?>>
|
|
||||||
<span class="color-swatch" style="background-color: <?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>;">
|
|
||||||
<span class="check-mark">✓</span>
|
|
||||||
</span>
|
|
||||||
<span class="color-name"><?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
|
||||||
</label>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Button Text Color</label>
|
|
||||||
|
|
||||||
<div class="color-palette">
|
|
||||||
<?php foreach ($buttonTextPalette as $colorValue => $colorLabel): ?>
|
|
||||||
<?php $isChecked = strtolower($colorValue) === $currentButtonTextColor; ?>
|
|
||||||
|
|
||||||
<label class="color-option" title="<?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?>">
|
|
||||||
<input
|
|
||||||
type="radio"
|
|
||||||
name="button_text_color"
|
|
||||||
value="<?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>"
|
|
||||||
<?= $isChecked ? 'checked' : ''; ?>>
|
|
||||||
<span class="color-swatch" style="background-color: <?= htmlspecialchars($colorValue, ENT_QUOTES, 'UTF-8'); ?>;">
|
|
||||||
<span class="check-mark">✓</span>
|
|
||||||
</span>
|
|
||||||
<span class="color-name"><?= htmlspecialchars($colorLabel, ENT_QUOTES, 'UTF-8'); ?></span>
|
|
||||||
</label>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Button Label</label>
|
|
||||||
<input type="text" name="button_label" class="form-control" value="<?php echo htmlspecialchars($template['button_label'] ?? 'Click Me'); ?>">
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
|
||||||
<label class="form-label">Select Client *</label>
|
|
||||||
<select name="client_id" id="clientSelect" class="form-control" required>
|
|
||||||
<option value="">Select a client...</option>
|
|
||||||
</select>
|
|
||||||
<span id="clientLoadingStatus" class="text-muted" style="margin-left: 10px; display: none;">Loading clients...</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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;">Loading schemas...</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<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 (($template['idroutine'] ?? '') == $routine['idroutine']) ? 'selected' : ''; ?>>
|
|
||||||
<?php echo htmlspecialchars($routine['name']); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<br>
|
|
||||||
<button type="submit" class="btn btn-primary"><?= htmlspecialchars($savechanges, ENT_QUOTES, 'UTF-8'); ?></button>
|
|
||||||
<a href="templates_dashboard.php" class="btn btn-secondary">Cancel</a>
|
|
||||||
</form>
|
|
||||||
</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'); ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
if (typeof jQuery === 'undefined') {
|
|
||||||
alert("Error: jQuery is not loaded.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const form = document.getElementById("editTemplateForm");
|
|
||||||
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");
|
|
||||||
|
|
||||||
const selectedClientId = <?php echo json_encode((int)($template['idclient'] ?? 0)); ?>;
|
|
||||||
const selectedSchemaId = <?php echo json_encode((int)($template['idschema'] ?? 0)); ?>;
|
|
||||||
|
|
||||||
$('#clientSelect').select2({
|
|
||||||
placeholder: "Search for a client...",
|
|
||||||
allowClear: true
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#schemaSelect').select2({
|
|
||||||
placeholder: "Search for a schema...",
|
|
||||||
allowClear: true
|
|
||||||
});
|
|
||||||
|
|
||||||
$('#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 isApiOrJson = selectedSource === 'API' || selectedSource === 'JSON';
|
|
||||||
|
|
||||||
if (isXls) {
|
|
||||||
headerRowWrapper.style.display = 'block';
|
|
||||||
startColumnWrapper.style.display = 'block';
|
|
||||||
xlsSheetNumberWrapper.style.display = 'block';
|
|
||||||
|
|
||||||
headerRow.required = true;
|
|
||||||
startColumn.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;
|
|
||||||
|
|
||||||
headerRow.disabled = true;
|
|
||||||
startColumn.disabled = true;
|
|
||||||
xlsSheetIndex.disabled = true;
|
|
||||||
|
|
||||||
if (isApiOrJson) {
|
|
||||||
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();
|
|
||||||
|
|
||||||
function formatClientLabel(client) {
|
|
||||||
const nome = client.Nominativo || "Name not available";
|
|
||||||
const id = client.IdCliente || "";
|
|
||||||
const codiceCliente = (client.CodiceCliente ?? client.codiceCliente ?? "").toString().trim();
|
|
||||||
const suffix = (codiceCliente.split("_")[1] || "").trim();
|
|
||||||
const shortCode = suffix || (codiceCliente ? codiceCliente.charAt(0) : "--");
|
|
||||||
|
|
||||||
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadClients() {
|
|
||||||
try {
|
|
||||||
clientLoadingStatus.style.display = 'inline';
|
|
||||||
clientLoadingStatus.textContent = 'Loading clients...';
|
|
||||||
|
|
||||||
const response = await fetch("get_clienti.php", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.error || `HTTP error: ${response.status}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const select = document.getElementById("clientSelect");
|
|
||||||
select.innerHTML = '<option value="">Select a client...</option>';
|
|
||||||
|
|
||||||
data.value.forEach(client => {
|
|
||||||
const id = client.IdCliente || "";
|
|
||||||
const option = new Option(formatClientLabel(client), id);
|
|
||||||
|
|
||||||
if (parseInt(id) === parseInt(selectedClientId)) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
select.add(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(select).trigger('change');
|
|
||||||
clientLoadingStatus.textContent = "Clients loaded.";
|
|
||||||
} catch (error) {
|
|
||||||
clientLoadingStatus.textContent = "Loading error.";
|
|
||||||
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Unable to load clients: " + error.message,
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setTimeout(() => clientLoadingStatus.style.display = 'none', 1500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadSchemas() {
|
|
||||||
try {
|
|
||||||
schemaLoadingStatus.style.display = 'inline';
|
|
||||||
schemaLoadingStatus.textContent = 'Loading schemas...';
|
|
||||||
|
|
||||||
const response = await fetch("get_schemi.php", {
|
|
||||||
method: "GET",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
throw new Error(data.error || `HTTP error: ${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'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
sortedSchemas.forEach(schema => {
|
|
||||||
const nome = schema.Nome || "Name not available";
|
|
||||||
const id = schema.IdSchemaCustomFields || "";
|
|
||||||
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
|
|
||||||
|
|
||||||
if (parseInt(id) === parseInt(selectedSchemaId)) {
|
|
||||||
option.selected = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
select.add(option);
|
|
||||||
});
|
|
||||||
|
|
||||||
$(select).trigger('change');
|
|
||||||
schemaLoadingStatus.textContent = "Schemas loaded.";
|
|
||||||
} catch (error) {
|
|
||||||
schemaLoadingStatus.textContent = "Loading error.";
|
|
||||||
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Unable to load schemas: " + error.message,
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
} finally {
|
|
||||||
setTimeout(() => schemaLoadingStatus.style.display = 'none', 1500);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadData() {
|
|
||||||
try {
|
|
||||||
await loadClients();
|
|
||||||
await loadSchemas();
|
|
||||||
} catch (error) {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Error while loading data: " + error.message,
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
let formData = new FormData(this);
|
|
||||||
|
|
||||||
const clientSelect = document.getElementById("clientSelect");
|
|
||||||
const clientId = clientSelect.value;
|
|
||||||
const selectedClientOption = clientSelect.options[clientSelect.selectedIndex];
|
|
||||||
|
|
||||||
if (!clientId) {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Please select a client.",
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let clientName = "";
|
|
||||||
if (selectedClientOption) {
|
|
||||||
const optionText = selectedClientOption.text.trim();
|
|
||||||
const nameMatch = optionText.match(/^(.+?)(?:\s*\(ID:\s*\d+\))?$/);
|
|
||||||
clientName = nameMatch ? nameMatch[1].trim() : optionText;
|
|
||||||
}
|
|
||||||
formData.append("client_name", clientName);
|
|
||||||
|
|
||||||
const schemaSelect = document.getElementById("schemaSelect");
|
|
||||||
const schemaId = schemaSelect.value;
|
|
||||||
const selectedSchemaOption = schemaSelect.options[schemaSelect.selectedIndex];
|
|
||||||
|
|
||||||
if (!schemaId) {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Please select a schema.",
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let schemaName = "";
|
|
||||||
if (selectedSchemaOption) {
|
|
||||||
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("schemaname", schemaName);
|
|
||||||
|
|
||||||
const routineId = routineSelect.value;
|
|
||||||
formData.append("idroutine", routineId);
|
|
||||||
|
|
||||||
const selectedSource = sourceType.value;
|
|
||||||
|
|
||||||
if ((selectedSource === 'API' || selectedSource === 'JSON') && !apiConfigSelect.value) {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Please select an API/JSON configuration.",
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selectedSource === 'XLS' && xlsSheetIndex.value === '') {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "Please enter the XLS sheet number.",
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
fetch("process_edit_template_xls.php", {
|
|
||||||
method: "POST",
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
if (data.success) {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Success!",
|
|
||||||
text: "Template updated successfully!",
|
|
||||||
icon: "success",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
}).then(() => {
|
|
||||||
window.location.href = "templates_dashboard.php";
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: data.message,
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(() => {
|
|
||||||
Swal.fire({
|
|
||||||
title: "Error!",
|
|
||||||
text: "An unexpected error occurred.",
|
|
||||||
icon: "error",
|
|
||||||
confirmButtonText: "OK"
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,333 +0,0 @@
|
|||||||
2025-06-10 12:21:44 - Errore nel recupero dati: HTTP 404, Risposta:
|
|
||||||
2025-06-16 11:15:30 - Errore nel recupero dati: HTTP 404, Risposta: {"title":"Not Found","status":404,"detail":"Not Found","instance":"GET /api/odata/Rapporto(2523026)","errorCode":"d25cbd678"}
|
|
||||||
2025-06-16 11:34:59 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to 93.43.5.102 port 443 after 21033 ms: Couldn't connect to server, Risposta:
|
|
||||||
2025-06-16 11:42:03 - Errore nella richiesta: Recv failure: Connection was reset
|
|
||||||
2025-07-04 10:42:49 - 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"}
|
|
||||||
2025-07-04 10:44:13 - 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"}
|
|
||||||
2025-07-04 10:48:07 - 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"}
|
|
||||||
2025-08-19 16:29:25 - 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"}
|
|
||||||
2025-08-26 16:47:19 - 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:48:15 - 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:48:44 - 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: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
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<?php
|
|
||||||
ini_set('log_errors', 1);
|
|
||||||
ini_set('error_log', __DIR__ . '/import_debug.log');
|
|
||||||
|
|
||||||
|
|
||||||
error_log("TEST: errore manuale");
|
|
||||||
@@ -1,555 +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
|
|
||||||
const eligible = [];
|
|
||||||
(window.gridData || []).forEach((row, index) => {
|
|
||||||
if (row.status !== 'l') {
|
|
||||||
eligible.push({ iddatadb: row.iddatadb, index, row: getGridRow(row.iddatadb) });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return eligible;
|
|
||||||
}
|
|
||||||
|
|
||||||
function hasUnsavedChanges() {
|
|
||||||
return (window.gridData || []).some(r => r._dirty);
|
|
||||||
}
|
|
||||||
|
|
||||||
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,657 +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') . '/';
|
|
||||||
|
|
||||||
// 🔹 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,
|
|
||||||
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.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'];
|
|
||||||
|
|
||||||
// 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;
|
|
||||||
$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
|
|
||||||
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
|
|
||||||
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'];
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
// 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"));
|
|
||||||
}
|
|
||||||
|
|
||||||
$campionePayload = [
|
|
||||||
"Commessa" => $commessaId,
|
|
||||||
"Matrice" => $matriceId,
|
|
||||||
"SottoMatrice" => null,
|
|
||||||
"SchemaCustomField" => $schemaId,
|
|
||||||
// "Riferimento" => $part["part_description"] ?? "",
|
|
||||||
"NoteWeb" => $part["part_description"] ?? "",
|
|
||||||
"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) {
|
|
||||||
$photoPath = $uploadDir . '/' . ltrim($photo['file_path'], './');
|
|
||||||
$fullPath = realpath($photoPath);
|
|
||||||
|
|
||||||
if (!$fullPath || !file_exists($fullPath)) {
|
|
||||||
$logContentPhotos .= "SKIP (file not found): {$photoPath}\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 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, and status
|
|
||||||
$stmt = $pdo->prepare("
|
|
||||||
UPDATE datadb
|
|
||||||
SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l'
|
|
||||||
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),
|
|
||||||
"message" => "Export successful",
|
|
||||||
"logFiles" => [
|
|
||||||
"step5_create" => $logFileStep5,
|
|
||||||
"step5_2_photos" => $logFilePhotos,
|
|
||||||
"step6_campioni" => $logFileStep6,
|
|
||||||
"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,
|
|
||||||
"step7_patch" => $logFileStep7 ?? null,
|
|
||||||
"step9_1_importa" => $logFileStep91 ?? null,
|
|
||||||
"step10_get" => $logFileStep10 ?? null
|
|
||||||
]
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Abilita il debug degli errori (solo per sviluppo)
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// Assicurati che non ci sia output prima del JSON
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
// Imposta l'header per JSON
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// Configura la tua chiave API di TrackingMore
|
|
||||||
$apiKey = 'u4ssgynn-xuyy-9act-ca29-2glsv2qlzh0w'; // La tua chiave API reale
|
|
||||||
|
|
||||||
// Funzione per inviare una risposta JSON e terminare l'esecuzione
|
|
||||||
function sendResponse($data)
|
|
||||||
{
|
|
||||||
ob_end_clean();
|
|
||||||
echo json_encode($data);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica che il numero di tracking e il corriere siano stati inviati
|
|
||||||
if (!isset($_POST['tracking_number']) || empty($_POST['tracking_number']) || !isset($_POST['courier_code']) || empty($_POST['courier_code'])) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Numero di tracking o corriere non fornito']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$trackingNumber = $_POST['tracking_number'];
|
|
||||||
$courierCode = $_POST['courier_code'];
|
|
||||||
|
|
||||||
// Lista dei corrieri validi per validazione
|
|
||||||
$validCarriers = ['tnt-it', 'dhl', 'gls', 'sda', 'ups'];
|
|
||||||
if (!in_array($courierCode, $validCarriers)) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Corriere non valido']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Imposta il nome del corriere in base al codice
|
|
||||||
$carrierNames = [
|
|
||||||
'tnt-it' => 'TNT Italy',
|
|
||||||
'dhl' => 'DHL',
|
|
||||||
'gls' => 'GLS',
|
|
||||||
'sda' => 'SDA',
|
|
||||||
'ups' => 'UPS'
|
|
||||||
];
|
|
||||||
$courierName = $carrierNames[$courierCode];
|
|
||||||
|
|
||||||
// Funzione per fare una richiesta cURL a TrackingMore
|
|
||||||
function makeRequest($url, $data, $apiKey, $method = 'POST')
|
|
||||||
{
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Tracking-Api-Key: ' . $apiKey,
|
|
||||||
'Content-Type: application/json'
|
|
||||||
]);
|
|
||||||
if ($method === 'POST') {
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
$jsonData = json_encode($data, JSON_PRETTY_PRINT);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $jsonData);
|
|
||||||
file_put_contents('debug.log', "Encoded JSON Data: $jsonData\n", FILE_APPEND);
|
|
||||||
}
|
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$error = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
file_put_contents('debug.log', "Request URL: $url\nRequest Data: " . json_encode($data) . "\nResponse: $response\nHTTP Code: $httpCode\nError: $error\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if ($response === false) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'code' => $httpCode,
|
|
||||||
'message' => 'Errore nella richiesta API: ' . $error
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$decodedResponse = json_decode($response, true);
|
|
||||||
if ($decodedResponse === null) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'code' => $httpCode,
|
|
||||||
'message' => 'Risposta API non valida (non è JSON)'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$decodedResponse['success'] = isset($decodedResponse['meta']['code']) && ($decodedResponse['meta']['code'] === 200 || $decodedResponse['meta']['code'] === 201);
|
|
||||||
$decodedResponse['http_code'] = $httpCode;
|
|
||||||
return $decodedResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Prova a creare il tracking
|
|
||||||
$createUrl = 'https://api.trackingmore.com/v4/trackings/create';
|
|
||||||
$createData = [
|
|
||||||
'tracking_number' => $trackingNumber,
|
|
||||||
'courier_code' => $courierCode
|
|
||||||
];
|
|
||||||
$createResponse = makeRequest($createUrl, $createData, $apiKey);
|
|
||||||
file_put_contents('debug.log', "Create Response: " . json_encode($createResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
$trackingInfo = null;
|
|
||||||
if ($createResponse['success']) {
|
|
||||||
// Creazione riuscita, usa i dati restituiti
|
|
||||||
$trackingInfo = $createResponse['data'];
|
|
||||||
} else {
|
|
||||||
// Controlla se l'errore è "Tracking No. already exists" (4101)
|
|
||||||
if (isset($createResponse['meta']['code']) && $createResponse['meta']['code'] === 4101) {
|
|
||||||
// Il tracking esiste già, usa /trackings (GET) con tracking_number e courier_code
|
|
||||||
$getUrl = 'https://api.trackingmore.com/v4/get?tracking_numbers=' . urlencode($trackingNumber);
|
|
||||||
$getResponse = makeRequest($getUrl, [], $apiKey, 'GET');
|
|
||||||
file_put_contents('debug.log', "Get Response: " . json_encode($getResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if ($getResponse['success'] && !empty($getResponse['data']['items']) && !empty($getResponse['data']['items'][0])) {
|
|
||||||
$trackingInfo = $getResponse['data']['items'][0];
|
|
||||||
} else {
|
|
||||||
$errorMessage = isset($getResponse['meta']['message']) ? $getResponse['meta']['message'] : 'Errore sconosciuto';
|
|
||||||
sendResponse(['success' => false, 'message' => 'Errore nel recupero del tracking esistente: ' . $errorMessage]);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Altro errore nella creazione
|
|
||||||
$errorMessage = isset($createResponse['meta']['message']) ? $createResponse['meta']['message'] : 'Errore sconosciuto';
|
|
||||||
sendResponse(['success' => false, 'message' => 'Errore nella creazione del tracking: ' . $errorMessage]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$trackingInfo) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Nessuna informazione di tracking trovata']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Estrai la data di consegna e il firmatario
|
|
||||||
$deliveryDate = 'Non disponibile';
|
|
||||||
$signedBy = 'Non disponibile';
|
|
||||||
if (isset($trackingInfo['origin_info']['trackinfo']) && is_array($trackingInfo['origin_info']['trackinfo'])) {
|
|
||||||
foreach ($trackingInfo['origin_info']['trackinfo'] as $checkpoint) {
|
|
||||||
if (isset($checkpoint['checkpoint_delivery_status']) && $checkpoint['checkpoint_delivery_status'] === 'delivered') {
|
|
||||||
$deliveryDate = $checkpoint['checkpoint_date'] ?? 'Non disponibile';
|
|
||||||
$signedBy = $trackingInfo['signed_by'] ?? 'Non disponibile';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restituisci i dati al frontend
|
|
||||||
sendResponse([
|
|
||||||
'success' => true,
|
|
||||||
'deliveryDate' => $deliveryDate,
|
|
||||||
'signedBy' => $signedBy,
|
|
||||||
'carrierName' => $courierName
|
|
||||||
]);
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Abilita il debug degli errori (solo per sviluppo)
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// Assicurati che non ci sia output prima del JSON
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
// Imposta l'header per JSON
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// Configura la tua chiave API di 17Track
|
|
||||||
$apiKey = '489F6B6DADDE09A5B6CB1C42B5363A3F'; // Sostituisci con la tua chiave API reale
|
|
||||||
|
|
||||||
// Funzione per inviare una risposta JSON e terminare l'esecuzione
|
|
||||||
function sendResponse($data)
|
|
||||||
{
|
|
||||||
ob_end_clean();
|
|
||||||
echo json_encode($data);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica che il numero di tracking sia stato inviato
|
|
||||||
if (!isset($_POST['tracking_number']) || empty($_POST['tracking_number'])) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Numero di tracking non fornito']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$trackingNumber = $_POST['tracking_number'];
|
|
||||||
|
|
||||||
// Funzione per fare una richiesta cURL a 17Track
|
|
||||||
function makeRequest($url, $data, $apiKey, $method = 'POST')
|
|
||||||
{
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'17token: ' . $apiKey,
|
|
||||||
'Content-Type: application/json'
|
|
||||||
]);
|
|
||||||
if ($method === 'POST') {
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
||||||
}
|
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$error = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
file_put_contents('debug.log', "Request URL: $url\nRequest Data: " . json_encode($data) . "\nResponse: $response\nHTTP Code: $httpCode\nError: $error\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if ($response === false || $httpCode !== 200) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'code' => $httpCode,
|
|
||||||
'message' => 'Errore nella richiesta API: HTTP ' . $httpCode . ' - ' . $error
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$decodedResponse = json_decode($response, true);
|
|
||||||
if ($decodedResponse === null) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'code' => $httpCode,
|
|
||||||
'message' => 'Risposta API non valida (non è JSON)'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
return $decodedResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Prova con auto-detection
|
|
||||||
$trackUrl = 'https://api.17track.net/track/v2/register';
|
|
||||||
$trackData = [
|
|
||||||
[
|
|
||||||
'number' => $trackingNumber,
|
|
||||||
'carrier' => null,
|
|
||||||
'auto_detection' => true
|
|
||||||
]
|
|
||||||
];
|
|
||||||
$registerResponse = makeRequest($trackUrl, $trackData, $apiKey);
|
|
||||||
|
|
||||||
file_put_contents('debug.log', "Register Response (Auto-detect): " . json_encode($registerResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if (!isset($registerResponse['data']['accepted']) || empty($registerResponse['data']['accepted'])) {
|
|
||||||
$errorMessage = $registerResponse['data']['rejected'][0]['error']['message'] ?? 'Errore sconosciuto';
|
|
||||||
sendResponse(['success' => false, 'message' => 'Errore nella registrazione del tracking: ' . $errorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Recupera i dettagli con riprova
|
|
||||||
$getUrl = 'https://api.17track.net/track/v2/gettrackinfo';
|
|
||||||
$getData = [['number' => $trackingNumber]];
|
|
||||||
$maxAttempts = 3;
|
|
||||||
$delaySeconds = 5;
|
|
||||||
|
|
||||||
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
|
|
||||||
$trackResponse = makeRequest($getUrl, $getData, $apiKey);
|
|
||||||
file_put_contents('debug.log', "Track Response (Attempt $attempt): " . json_encode($trackResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if (isset($trackResponse['data']['accepted']) && !empty($trackResponse['data']['accepted'])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($attempt < $maxAttempts) {
|
|
||||||
sleep($delaySeconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Se auto-detection ha successo, procedi
|
|
||||||
if (isset($trackResponse['data']['accepted']) && !empty($trackResponse['data']['accepted'])) {
|
|
||||||
$trackingInfo = $trackResponse['data']['accepted'][0]['track'] ?? null;
|
|
||||||
if (!$trackingInfo) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Nessuna informazione di tracking trovata']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$deliveryDate = 'Non disponibile';
|
|
||||||
$signedBy = 'Non disponibile';
|
|
||||||
$carrierName = $trackingInfo['carrier']['name'] ?? 'Sconosciuto';
|
|
||||||
|
|
||||||
if (isset($trackingInfo['z0']['e']) && $trackingInfo['z0']['e'] == 40) {
|
|
||||||
$deliveryDate = $trackingInfo['z0']['z'] ?? 'Non disponibile';
|
|
||||||
$signedBy = $trackingInfo['z0']['d'] ?? 'Non disponibile';
|
|
||||||
}
|
|
||||||
|
|
||||||
sendResponse([
|
|
||||||
'success' => true,
|
|
||||||
'deliveryDate' => $deliveryDate,
|
|
||||||
'signedBy' => $signedBy,
|
|
||||||
'carrierName' => $carrierName
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Se auto-detection fallisce, prova una lista di corrieri comuni
|
|
||||||
$commonCarriers = [
|
|
||||||
['code' => 100003, 'name' => 'TNT'], // TNT
|
|
||||||
['code' => 100001, 'name' => 'DHL'], // DHL Express
|
|
||||||
['code' => 100065, 'name' => 'DHL eCommerce'], // DHL eCommerce
|
|
||||||
['code' => 100002, 'name' => 'UPS'] // UPS
|
|
||||||
];
|
|
||||||
|
|
||||||
$possibleCarriers = [];
|
|
||||||
foreach ($commonCarriers as $carrier) {
|
|
||||||
$trackData = [
|
|
||||||
[
|
|
||||||
'number' => $trackingNumber,
|
|
||||||
'carrier' => $carrier['code'],
|
|
||||||
'auto_detection' => false
|
|
||||||
]
|
|
||||||
];
|
|
||||||
$registerResponse = makeRequest($trackUrl, $trackData, $apiKey);
|
|
||||||
|
|
||||||
if (isset($registerResponse['data']['accepted']) && !empty($registerResponse['data']['accepted'])) {
|
|
||||||
$possibleCarriers[] = $carrier['name'];
|
|
||||||
}
|
|
||||||
sleep(1); // Piccolo ritardo per evitare limiti di rate
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!empty($possibleCarriers)) {
|
|
||||||
sendResponse([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Auto-detection fallita. Seleziona un corriere tra quelli possibili.',
|
|
||||||
'possibleCarriers' => $possibleCarriers
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Nessun corriere identificato per questo numero di tracking']);
|
|
||||||
}
|
|
||||||
@@ -1,166 +0,0 @@
|
|||||||
<?php
|
|
||||||
// Abilita il debug degli errori (solo per sviluppo)
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
// Assicurati che non ci sia output prima del JSON
|
|
||||||
ob_start();
|
|
||||||
|
|
||||||
// Imposta l'header per JSON
|
|
||||||
header('Content-Type: application/json');
|
|
||||||
|
|
||||||
// Configura la tua chiave API di TrackingMore
|
|
||||||
$apiKey = 'u4ssgynn-xuyy-9act-ca29-2glsv2qlzh0w'; // La tua chiave API reale
|
|
||||||
|
|
||||||
// Funzione per inviare una risposta JSON e terminare l'esecuzione
|
|
||||||
function sendResponse($data)
|
|
||||||
{
|
|
||||||
ob_end_clean();
|
|
||||||
echo json_encode($data);
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Verifica che il numero di tracking sia stato inviato
|
|
||||||
if (!isset($_POST['tracking_number']) || empty($_POST['tracking_number'])) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Numero di tracking non fornito']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$trackingNumber = $_POST['tracking_number'];
|
|
||||||
|
|
||||||
// Funzione per fare una richiesta cURL a TrackingMore
|
|
||||||
function makeRequest($url, $data, $apiKey, $method = 'POST')
|
|
||||||
{
|
|
||||||
$ch = curl_init($url);
|
|
||||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
|
||||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
|
||||||
'Tracking-Api-Key: ' . $apiKey,
|
|
||||||
'Content-Type: application/json'
|
|
||||||
]);
|
|
||||||
if ($method === 'POST') {
|
|
||||||
curl_setopt($ch, CURLOPT_POST, true);
|
|
||||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
|
|
||||||
}
|
|
||||||
curl_setopt($ch, CURLOPT_TIMEOUT, 10);
|
|
||||||
$response = curl_exec($ch);
|
|
||||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
|
||||||
$error = curl_error($ch);
|
|
||||||
curl_close($ch);
|
|
||||||
|
|
||||||
file_put_contents('debug.log', "Request URL: $url\nRequest Data: " . json_encode($data) . "\nResponse: $response\nHTTP Code: $httpCode\nError: $error\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if ($response === false || ($httpCode !== 200 && $httpCode !== 201)) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'code' => $httpCode,
|
|
||||||
'message' => 'Errore nella richiesta API: HTTP ' . $httpCode . ' - ' . $error
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$decodedResponse = json_decode($response, true);
|
|
||||||
if ($decodedResponse === null) {
|
|
||||||
return [
|
|
||||||
'success' => false,
|
|
||||||
'code' => $httpCode,
|
|
||||||
'message' => 'Risposta API non valida (non è JSON)'
|
|
||||||
];
|
|
||||||
}
|
|
||||||
|
|
||||||
$decodedResponse['success'] = isset($decodedResponse['meta']['code']) && ($decodedResponse['meta']['code'] === 200 || $decodedResponse['meta']['code'] === 201);
|
|
||||||
return $decodedResponse;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 1: Identifica il corriere
|
|
||||||
$detectUrl = 'https://api.trackingmore.com/v4/couriers/detect';
|
|
||||||
$detectData = ['tracking_number' => $trackingNumber];
|
|
||||||
$detectResponse = makeRequest($detectUrl, $detectData, $apiKey);
|
|
||||||
file_put_contents('debug.log', "Detect Response: " . json_encode($detectResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if (!$detectResponse['success']) {
|
|
||||||
$errorMessage = isset($detectResponse['meta']['message']) ? $detectResponse['meta']['message'] : 'Errore sconosciuto';
|
|
||||||
sendResponse(['success' => false, 'message' => 'Errore nella rilevazione del corriere: ' . $errorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$couriers = $detectResponse['data'] ?? [];
|
|
||||||
if (empty($couriers)) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Corriere non identificato per il numero di tracking']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prendi il primo corriere per ora
|
|
||||||
$courierCode = $couriers[0]['courier_code'] ?? null;
|
|
||||||
$courierName = $couriers[0]['courier_name'] ?? 'Sconosciuto';
|
|
||||||
if (!$courierCode) {
|
|
||||||
sendResponse(['success' => false, 'message' => 'Corriere non identificato']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 2: Crea un tracking
|
|
||||||
$createUrl = 'https://api.trackingmore.com/v4/trackings/create';
|
|
||||||
$createData = [
|
|
||||||
'tracking_number' => $trackingNumber,
|
|
||||||
'courier_code' => $courierCode
|
|
||||||
];
|
|
||||||
$createResponse = makeRequest($createUrl, $createData, $apiKey);
|
|
||||||
file_put_contents('debug.log', "Create Response: " . json_encode($createResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if (!$createResponse['success']) {
|
|
||||||
$errorMessage = isset($createResponse['meta']['message']) ? $createResponse['meta']['message'] : 'Errore sconosciuto';
|
|
||||||
sendResponse(['success' => false, 'message' => 'Errore nella creazione del tracking: ' . $errorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Step 3: Recupera i dettagli del tracking con riprova
|
|
||||||
$getUrl = 'https://api.trackingmore.com/v4/trackings/get?tracking_number=' . urlencode($trackingNumber);
|
|
||||||
$maxAttempts = 3;
|
|
||||||
$delaySeconds = 5;
|
|
||||||
|
|
||||||
for ($attempt = 1; $attempt <= $maxAttempts; $attempt++) {
|
|
||||||
$trackResponse = makeRequest($getUrl, [], $apiKey, 'GET');
|
|
||||||
file_put_contents('debug.log', "Track Response (Attempt $attempt): " . json_encode($trackResponse) . "\n", FILE_APPEND);
|
|
||||||
|
|
||||||
if ($trackResponse['success'] && !empty($trackResponse['data'])) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($attempt < $maxAttempts) {
|
|
||||||
sleep($delaySeconds);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$trackResponse['success']) {
|
|
||||||
$errorMessage = isset($trackResponse['meta']['message']) ? $trackResponse['meta']['message'] : 'Errore sconosciuto';
|
|
||||||
sendResponse(['success' => false, 'message' => 'Errore nel recupero delle informazioni: ' . $errorMessage]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$trackingInfo = $trackResponse['data'] ?? null;
|
|
||||||
if (!$trackingInfo) {
|
|
||||||
// Se ci sono più corrieri rilevati, restituisci una lista per la tendina
|
|
||||||
if (count($couriers) > 1) {
|
|
||||||
$possibleCarriers = array_map(function ($courier) {
|
|
||||||
return ['code' => $courier['courier_code'], 'name' => $courier['courier_name']];
|
|
||||||
}, $couriers);
|
|
||||||
sendResponse([
|
|
||||||
'success' => false,
|
|
||||||
'message' => 'Dati non trovati. Seleziona un corriere tra quelli rilevati.',
|
|
||||||
'possibleCarriers' => $possibleCarriers
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
sendResponse(['success' => false, 'message' => 'Nessuna informazione di tracking trovata']);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Estrai la data di consegna e il firmatario
|
|
||||||
$deliveryDate = 'Non disponibile';
|
|
||||||
$signedBy = 'Non disponibile';
|
|
||||||
foreach ($trackingInfo['trackinfo'] as $checkpoint) {
|
|
||||||
if ($checkpoint['status'] === 'delivered') {
|
|
||||||
$deliveryDate = $checkpoint['Date'];
|
|
||||||
$signedBy = $checkpoint['signed_by'] ?? 'Non disponibile';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Restituisci i dati al frontend
|
|
||||||
sendResponse([
|
|
||||||
'success' => true,
|
|
||||||
'deliveryDate' => $deliveryDate,
|
|
||||||
'signedBy' => $signedBy,
|
|
||||||
'carrierName' => $courierName
|
|
||||||
]);
|
|
||||||
@@ -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,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 {
|
|
||||||
$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();
|
|
||||||
|
|
||||||
$webOnly = isset($_GET['web_only']) ? (int)$_GET['web_only'] : 1;
|
|
||||||
|
|
||||||
$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;
|
|
||||||
|
|
||||||
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)
|
|
||||||
);
|
|
||||||
|
|
||||||
if ($debug === 1) {
|
|
||||||
echo json_encode([
|
|
||||||
'endpoint' => $endpoint,
|
|
||||||
'full_url' => $full_url,
|
|
||||||
'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 +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',
|
|
||||||
'$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,39 +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();
|
|
||||||
|
|
||||||
// ID della CommessaWeb specifica (cambialo di volta in volta)
|
|
||||||
$id = 529435; // TODO: Cambia questo valore con l'ID desiderato
|
|
||||||
|
|
||||||
// Endpoint per recuperare la CommessaWeb specifica con espansione dello schema custom
|
|
||||||
$endpoint = "CommessaWeb({$id})";
|
|
||||||
|
|
||||||
// Opzioni per l'espansione: includi OrderCustomFields per ottenere i campi custom dello schema assegnato all'ordine
|
|
||||||
$options = ['$expand' => 'OrderCustomFields'];
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Salva il JSON in locale
|
|
||||||
file_put_contents(__DIR__ . '/commessaweb_schema_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();
|
|
||||||
|
|
||||||
// მივიღოთ მრავლობითი field_ids
|
|
||||||
$fieldIds = [];
|
|
||||||
if (isset($_GET['field_ids'])) {
|
|
||||||
$fieldIds = array_filter(array_map('intval', explode(',', $_GET['field_ids'])));
|
|
||||||
}
|
|
||||||
|
|
||||||
// თუ არ გადმოგვცეს -> ერთი default
|
|
||||||
if (empty($fieldIds)) {
|
|
||||||
$fieldIds = [156];
|
|
||||||
}
|
|
||||||
|
|
||||||
$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));
|
|
||||||
}
|
|
||||||
|
|
||||||
echo json_encode($results);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode([
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -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,36 +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 Matrici
|
|
||||||
$endpoint = 'Matrice';
|
|
||||||
|
|
||||||
// (Opzionale) aggiungi parametri, ad esempio $top per limitare i risultati
|
|
||||||
$options = []; // oppure ad esempio: ['$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);
|
|
||||||
|
|
||||||
// Salva il JSON in locale
|
|
||||||
file_put_contents(__DIR__ . '/matrici_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,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,37 +0,0 @@
|
|||||||
<?php
|
|
||||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
|
||||||
require_once dirname(__FILE__) . '/class/VisualLimsApiClientXml.class.php';
|
|
||||||
|
|
||||||
header('Content-Type: application/xml; charset=utf-8');
|
|
||||||
|
|
||||||
ini_set('display_errors', '0');
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$api = VisualLimsApiClientXml::getInstance();
|
|
||||||
|
|
||||||
// Endpoint per i metadata
|
|
||||||
$endpoint = '$metadata';
|
|
||||||
|
|
||||||
// Nessun parametro aggiuntivo necessario
|
|
||||||
$options = [];
|
|
||||||
|
|
||||||
// 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);
|
|
||||||
|
|
||||||
// Salva il file XML in locale
|
|
||||||
file_put_contents(__DIR__ . '/metadata_response.xml', $data);
|
|
||||||
|
|
||||||
// Restituisci il contenuto XML
|
|
||||||
echo $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 '<?xml version="1.0" encoding="utf-8"?><error>' . htmlspecialchars($e->getMessage()) . '</error>';
|
|
||||||
}
|
|
||||||
@@ -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);
|
|
||||||
}
|
|
||||||
@@ -1,141 +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();
|
|
||||||
|
|
||||||
// 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
|
|
||||||
|
|
||||||
$codiceRapporto = trim($_GET['codice'] ?? '');
|
|
||||||
$step = trim($_GET['step'] ?? 'base');
|
|
||||||
|
|
||||||
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);
|
|
||||||
} 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([
|
|
||||||
'success' => false,
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
||||||
}
|
|
||||||
@@ -1,30 +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();
|
|
||||||
|
|
||||||
// ID dello schema passato via GET o default 45
|
|
||||||
$schemaId = isset($_GET['id']) && is_numeric($_GET['id']) ? intval($_GET['id']) : 42;
|
|
||||||
|
|
||||||
// IMPORTANTE: $expand va dentro la stringa endpoint per entità singola
|
|
||||||
$endpoint = "SchemaCustomField($schemaId)?\$expand=SchemiCustomFieldsDettagli(\$expand=CustomField)";
|
|
||||||
|
|
||||||
// Nessun parametro aggiuntivo
|
|
||||||
$data = $api->get($endpoint);
|
|
||||||
|
|
||||||
// Salva la risposta per debug
|
|
||||||
file_put_contents(__DIR__ . '/schema_dettagli_response.json', json_encode($data));
|
|
||||||
|
|
||||||
echo json_encode($data);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
echo json_encode([
|
|
||||||
'error' => $e->getMessage()
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
@@ -1,36 +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();
|
|
||||||
|
|
||||||
// Nessun filtro o espansione: solo la lista degli schemi
|
|
||||||
$endpoint = 'SchemaCustomField';
|
|
||||||
|
|
||||||
// (Opzionale) aggiungi $top se vuoi limitare i risultati
|
|
||||||
$options = []; // oppure ad esempio: ['$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);
|
|
||||||
|
|
||||||
// Salva il JSON in locale
|
|
||||||
file_put_contents(__DIR__ . '/schemi_base_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,26 +0,0 @@
|
|||||||
```php
|
|
||||||
<?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');
|
|
||||||
|
|
||||||
// Disabilita la visualizzazione degli errori PHP per evitare output HTML
|
|
||||||
ini_set('display_errors', '0');
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
|
|
||||||
try {
|
|
||||||
$api = VisualLimsApiClient::getInstance();
|
|
||||||
$data = $api->get("SchemaCustomField"); // Recupera la lista degli schemi custom fields
|
|
||||||
|
|
||||||
// Salva la risposta in un file per debug
|
|
||||||
file_put_contents(__DIR__ . '/schemi_custom_fields_response.json', json_encode($data));
|
|
||||||
|
|
||||||
echo json_encode($data);
|
|
||||||
} catch (Exception $e) {
|
|
||||||
http_response_code(500);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,161 +0,0 @@
|
|||||||
<?php
|
|
||||||
ini_set('display_errors', 1);
|
|
||||||
ini_set('display_startup_errors', 1);
|
|
||||||
error_reporting(E_ALL);
|
|
||||||
ini_set('log_errors', 1);
|
|
||||||
ini_set('error_log', __DIR__ . '/import_debug.log');
|
|
||||||
if (!file_exists(__DIR__ . '/import_debug.log')) {
|
|
||||||
file_put_contents(__DIR__ . '/import_debug.log', "Inizio importazione alle " . date('Y-m-d H:i:s') . "\n", FILE_APPEND);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Log iniziale
|
|
||||||
error_log("Inizio importazione alle " . date('Y-m-d H:i:s'));
|
|
||||||
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST' || !isset($_POST['template_id']) || !isset($_POST['selected_rows']) || !isset($_POST['filename'])) {
|
|
||||||
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'];
|
|
||||||
|
|
||||||
$_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;
|
|
||||||
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
|
|
||||||
// Genera un UUID univoco per importreferencecode
|
|
||||||
$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->execute([$template_id]);
|
|
||||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (empty($allMappings)) {
|
|
||||||
header("Location: import_xls.php?id=$template_id&status=error&message=" . urlencode("Nessun mapping trovato per il template"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Trova il campo main_field
|
|
||||||
$mainFieldMapping = null;
|
|
||||||
foreach ($allMappings as $mapping) {
|
|
||||||
if ($mapping['main_field'] == 1) {
|
|
||||||
$mainFieldMapping = $mapping;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserisci le righe selezionate in datadb
|
|
||||||
$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;
|
|
||||||
|
|
||||||
$values = [
|
|
||||||
$template_id,
|
|
||||||
$importReferenceCode,
|
|
||||||
$newFilename,
|
|
||||||
'i',
|
|
||||||
$user_id,
|
|
||||||
null,
|
|
||||||
date('Y-m-d'),
|
|
||||||
$excelrow,
|
|
||||||
$default_idclient // Aggiungi idclient
|
|
||||||
];
|
|
||||||
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow, idclient) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
|
||||||
$stmt = $pdo->prepare($sql);
|
|
||||||
$stmt->execute($values);
|
|
||||||
|
|
||||||
$iddatadb = $pdo->lastInsertId();
|
|
||||||
$insertedIds[] = $iddatadb;
|
|
||||||
|
|
||||||
// Inserisci tutti i campi in import_data_details
|
|
||||||
foreach ($allMappings as $mapping) {
|
|
||||||
$fieldValue = null;
|
|
||||||
if (!$mapping['is_manual']) {
|
|
||||||
$excelColumn = trim($mapping['excel_column']);
|
|
||||||
$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));
|
|
||||||
} else {
|
|
||||||
$fieldValue = $mapping['manual_default'] ?? '';
|
|
||||||
error_log("Excel column '$excelColumn' not found or empty, using default: " . var_export($fieldValue, true));
|
|
||||||
}
|
|
||||||
switch ($mapping['data_type']) {
|
|
||||||
case 'INT':
|
|
||||||
$fieldValue = is_numeric($fieldValue) ? (int)$fieldValue : ($mapping['manual_default'] ?? 0);
|
|
||||||
break;
|
|
||||||
case 'DATE':
|
|
||||||
$fieldValue = !empty($fieldValue) ? date('Y-m-d', strtotime($fieldValue)) : ($mapping['manual_default'] === 'today' ? date('Y-m-d') : ($mapping['manual_default'] ?? ''));
|
|
||||||
break;
|
|
||||||
case 'CHAR':
|
|
||||||
$fieldValue = !empty($fieldValue) ? substr((string)$fieldValue, 0, 1) : ($mapping['manual_default'] ?? '');
|
|
||||||
break;
|
|
||||||
case 'Testo':
|
|
||||||
case 'VARCHAR':
|
|
||||||
default:
|
|
||||||
$fieldValue = !empty($fieldValue) ? (string)$fieldValue : ($mapping['manual_default'] ?? '');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$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']);
|
|
||||||
}
|
|
||||||
error_log("Inserting into import_data_details - Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true) . ", Is Manual: " . $mapping['is_manual'] . ", Excel Column: " . ($mapping['excel_column'] ?? 'N/A') . ", Manual Default: " . ($mapping['manual_default'] ?? 'N/A'));
|
|
||||||
$stmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
|
||||||
$stmt->execute([$iddatadb, $mapping['id'], $fieldValue]);
|
|
||||||
error_log("Inserted into import_data_details for ID $iddatadb, Mapping ID: " . $mapping['id'] . ", Field Value: " . var_export($fieldValue, true));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$_SESSION['inserted_ids'] = $insertedIds;
|
|
||||||
|
|
||||||
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
|
||||||
exit;
|
|
||||||
?>
|
|
||||||
@@ -1,305 +0,0 @@
|
|||||||
<?php
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
// Controlla se è stato passato un ID valido
|
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = intval($_GET['id']); // Sanifica l'ID
|
|
||||||
|
|
||||||
// Recupera il template dal database
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
|
||||||
$stmt->execute([$id]);
|
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$template) {
|
|
||||||
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Debug del template
|
|
||||||
error_log("Loaded template: " . print_r($template, true));
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<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'); ?>
|
|
||||||
<style>
|
|
||||||
.table-container {
|
|
||||||
overflow-x: auto;
|
|
||||||
max-width: 100%;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th,
|
|
||||||
.table td {
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
border: 1px solid #dee2e6;
|
|
||||||
min-width: 100px;
|
|
||||||
max-width: 200px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th:first-child,
|
|
||||||
.table td:first-child {
|
|
||||||
min-width: 50px;
|
|
||||||
max-width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th {
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
position: relative;
|
|
||||||
cursor: col-resize;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th .resize-handle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 5px;
|
|
||||||
height: 100%;
|
|
||||||
cursor: col-resize;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th .resize-handle:hover {
|
|
||||||
background: #007bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container input {
|
|
||||||
width: 300px;
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid #ced4da;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
display: none;
|
|
||||||
border: 4px solid #f3f3f3;
|
|
||||||
border-top: 4px solid #3498db;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
transform: rotate(360deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
<title><?= htmlspecialchars($template['name']) ?> - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></title>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<?php include('include/navbar.php'); ?>
|
|
||||||
<?php include('include/topbar.php'); ?>
|
|
||||||
<div class="page-wrapper">
|
|
||||||
<div class="page-content">
|
|
||||||
<?php include('top_stat_widget.php'); ?>
|
|
||||||
|
|
||||||
<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 ?>, Start Row: <?= $template['header_row'] ?>, Start Column: <?= $template['start_column'] ?></small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body">
|
|
||||||
<!-- 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">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>
|
|
||||||
</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-->
|
|
||||||
|
|
||||||
<!-- 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() {
|
|
||||||
const form = document.getElementById('uploadForm');
|
|
||||||
const loader = document.getElementById('loader');
|
|
||||||
const errorContainer = document.getElementById('errorContainer');
|
|
||||||
const tableContainer = document.getElementById('tableContainer');
|
|
||||||
|
|
||||||
form.addEventListener('submit', function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
loader.style.display = 'block';
|
|
||||||
errorContainer.style.display = 'none';
|
|
||||||
tableContainer.innerHTML = '';
|
|
||||||
|
|
||||||
const formData = new FormData(this);
|
|
||||||
formData.append('template_id', <?= $id ?>);
|
|
||||||
formData.append('header_row', <?= $template['header_row'] ?>);
|
|
||||||
formData.append('start_column', <?= $template['start_column'] ?>);
|
|
||||||
|
|
||||||
fetch('process_import_xls.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
loader.style.display = 'none';
|
|
||||||
if (data.error) {
|
|
||||||
errorContainer.textContent = data.error;
|
|
||||||
errorContainer.style.display = 'block';
|
|
||||||
} else {
|
|
||||||
let html = `
|
|
||||||
<form id="selectRowsForm" action="import_edit.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>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;
|
|
||||||
|
|
||||||
// Aggiungi logica per il ridimensionamento delle colonne
|
|
||||||
const thElements = document.querySelectorAll('.table th');
|
|
||||||
thElements.forEach((th, index) => {
|
|
||||||
if (index === 0) return; // Escludi la colonna "Seleziona" dal ridimensionamento
|
|
||||||
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');
|
|
||||||
const checkboxes = document.querySelectorAll('.row-checkbox');
|
|
||||||
const proceedButton = document.getElementById('proceedButton');
|
|
||||||
|
|
||||||
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';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Aggiungi event listener per i checkbox
|
|
||||||
checkboxes.forEach(checkbox => {
|
|
||||||
checkbox.addEventListener('change', function() {
|
|
||||||
proceedButton.disabled = !Array.from(checkboxes).some(cb => cb.checked);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(error => {
|
|
||||||
loader.style.display = 'none';
|
|
||||||
errorContainer.textContent = 'Errore durante il caricamento del file: ' + error.message;
|
|
||||||
errorContainer.style.display = 'block';
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
@@ -1,599 +0,0 @@
|
|||||||
<?php
|
|
||||||
include('include/headscript.php');
|
|
||||||
|
|
||||||
// Controlla se è stato passato un ID valido
|
|
||||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
|
||||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
$id = intval($_GET['id']); // Sanifica l'ID
|
|
||||||
|
|
||||||
// Recupera il template dal database
|
|
||||||
$db = DBHandlerSelect::getInstance();
|
|
||||||
$pdo = $db->getConnection();
|
|
||||||
$stmt = $pdo->prepare("SELECT * FROM excel_templates WHERE id = ?");
|
|
||||||
$stmt->execute([$id]);
|
|
||||||
$template = $stmt->fetch(PDO::FETCH_ASSOC);
|
|
||||||
|
|
||||||
if (!$template) {
|
|
||||||
header("Location: template_dashboard.php?status=error&message=" . urlencode("Template not found"));
|
|
||||||
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));
|
|
||||||
?>
|
|
||||||
|
|
||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<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%;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table {
|
|
||||||
width: 100%;
|
|
||||||
border-collapse: collapse;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th,
|
|
||||||
.table td {
|
|
||||||
padding: 10px;
|
|
||||||
text-align: left;
|
|
||||||
border: 1px solid #dee2e6;
|
|
||||||
min-width: 100px;
|
|
||||||
max-width: 200px;
|
|
||||||
overflow: hidden;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
white-space: nowrap;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th:first-child,
|
|
||||||
.table td:first-child {
|
|
||||||
min-width: 50px;
|
|
||||||
max-width: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th {
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
position: relative;
|
|
||||||
cursor: col-resize;
|
|
||||||
user-select: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th .resize-handle {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
width: 5px;
|
|
||||||
height: 100%;
|
|
||||||
cursor: col-resize;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table th .resize-handle:hover {
|
|
||||||
background: #007bff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container {
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.search-container input {
|
|
||||||
width: 300px;
|
|
||||||
padding: 8px;
|
|
||||||
border: 1px solid #ced4da;
|
|
||||||
border-radius: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.loader {
|
|
||||||
display: none;
|
|
||||||
border: 4px solid #f3f3f3;
|
|
||||||
border-top: 4px solid #3498db;
|
|
||||||
border-radius: 50%;
|
|
||||||
width: 30px;
|
|
||||||
height: 30px;
|
|
||||||
animation: spin 1s linear infinite;
|
|
||||||
margin: 10px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes spin {
|
|
||||||
0% {
|
|
||||||
transform: rotate(0deg);
|
|
||||||
}
|
|
||||||
|
|
||||||
100% {
|
|
||||||
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>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<div class="wrapper">
|
|
||||||
<?php include('include/navbar.php'); ?>
|
|
||||||
<?php include('include/topbar.php'); ?>
|
|
||||||
<div class="page-wrapper">
|
|
||||||
<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>
|
|
||||||
</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>
|
|
||||||
</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 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>
|
|
||||||
<div class="loader" id="loader"></div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<div id="errorContainer" class="alert alert-danger mt-3" style="display: none;"></div>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<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>
|
|
||||||
|
|
||||||
<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>
|
|
||||||
<?php include('jsinclude.php'); ?>
|
|
||||||
<script>
|
|
||||||
document.addEventListener("DOMContentLoaded", function() {
|
|
||||||
const form = document.getElementById('uploadForm');
|
|
||||||
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();
|
|
||||||
loader.style.display = 'block';
|
|
||||||
errorContainer.style.display = 'none';
|
|
||||||
tableContainer.innerHTML = '';
|
|
||||||
|
|
||||||
const formData = new FormData(this);
|
|
||||||
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) ?>);
|
|
||||||
|
|
||||||
fetch('process_import_xls2.php', {
|
|
||||||
method: 'POST',
|
|
||||||
body: formData
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
console.log('Stato risposta:', response.status);
|
|
||||||
return 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);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.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>
|
|
||||||
File diff suppressed because it is too large
Load Diff
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user