Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 66ab44142b | |||
| be450f9d85 | |||
| 2a6eddeb44 | |||
| 4d447a5c0d | |||
| c6168dd5d4 | |||
| 1c2ea459ca | |||
| 7183189343 | |||
| 1bb9928b24 | |||
| 173aef51c6 | |||
| a94eac9fb4 | |||
| 51cfd0a434 | |||
| def2715d38 | |||
| 68f344bc06 | |||
| e3f7637b64 | |||
| f4ea06fb93 | |||
| 2ab667df22 | |||
| 485748859e | |||
| 7b961d5818 | |||
| 500e40782d | |||
| b19963808e | |||
| 8a5859ce46 | |||
| 3d2c0c9799 | |||
| 0130231c64 | |||
| a99810d61b | |||
| 7473973a34 | |||
| 1947730e34 | |||
| e3b1a46482 | |||
| 0e16812e9a | |||
| 40dd23e658 | |||
| bd47f4f715 | |||
| 042b4698fb | |||
| 614bc4feba | |||
| bef600ae11 | |||
| 1476160a38 | |||
| 2c7ddbcfeb | |||
| 6ad10c4d8f | |||
| e0654b4847 | |||
| fda3d66d1f | |||
| ff61456d91 | |||
| 089886cb9a | |||
| 4dc010d80e | |||
| 83c6157b10 |
+2
-1
@@ -60,4 +60,5 @@ public/userarea/schemi_base_response.json
|
||||
# Ignora tutti i log ovunque
|
||||
*.log
|
||||
|
||||
public/userarea/cache/
|
||||
public/userarea/cache/
|
||||
public/userarea/error_log.txt
|
||||
|
||||
@@ -109,6 +109,8 @@ class LoginController extends Controller
|
||||
// Reindirizza in base al ruolo
|
||||
if ($user->hasRole('Admin')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
} elseif ($user->hasRole('SuperUser')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
} elseif ($user->hasRole('User')) {
|
||||
return redirect()->to('userarea/import_dashboard.php');
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
"phpmailer/phpmailer": "^6.9",
|
||||
"phpoffice/phpspreadsheet": "^4.1",
|
||||
"proengsoft/laravel-jsvalidation": "^4.0.0",
|
||||
"robmorgan/phinx": "^0.16.12",
|
||||
"socialiteproviders/microsoft": "^4.7",
|
||||
"spatie/laravel-query-builder": "^5.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",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "9c4f1e3bc3ee2180211c055e70635aef",
|
||||
"content-hash": "557df76fdde549210349be369d479f5d",
|
||||
"packages": [
|
||||
{
|
||||
"name": "akaunting/laravel-setting",
|
||||
@@ -251,6 +251,330 @@
|
||||
],
|
||||
"time": "2023-11-29T23:19:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/chronos",
|
||||
"version": "3.5.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/chronos.git",
|
||||
"reference": "e6e777b534244911566face8a5dbdbd7f7bda5a6"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/chronos/zipball/e6e777b534244911566face8a5dbdbd7f7bda5a6",
|
||||
"reference": "e6e777b534244911566face8a5dbdbd7f7bda5a6",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.1",
|
||||
"psr/clock": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/clock-implementation": "1.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cakephp-codesniffer": "^5.0",
|
||||
"phpunit/phpunit": "^10.5.58 || ^11.5.3 || ^12.1.3"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Chronos\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Brian Nesbitt",
|
||||
"email": "brian@nesbot.com",
|
||||
"homepage": "http://nesbot.com"
|
||||
},
|
||||
{
|
||||
"name": "The CakePHP Team",
|
||||
"homepage": "https://cakephp.org"
|
||||
}
|
||||
],
|
||||
"description": "A simple API extension for DateTime.",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"date",
|
||||
"datetime",
|
||||
"time"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/chronos/issues",
|
||||
"source": "https://github.com/cakephp/chronos"
|
||||
},
|
||||
"time": "2026-04-10T02:50:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/core",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/core.git",
|
||||
"reference": "9c458b0e9322ec88bc4c758b33cde6a0abf49d12"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/core/zipball/9c458b0e9322ec88bc4c758b33cde6a0abf49d12",
|
||||
"reference": "9c458b0e9322ec88bc4c758b33cde6a0abf49d12",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/utility": "^5.3.0",
|
||||
"league/container": "^5.1",
|
||||
"php": ">=8.2",
|
||||
"psr/container": "^1.1 || ^2.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^2.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/cache": "To use Configure::store() and restore().",
|
||||
"cakephp/event": "To use PluginApplicationInterface or plugin applications.",
|
||||
"league/container": "To use Container and ServiceProvider classes"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"functions.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Cake\\Core\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/core/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "CakePHP Framework Core classes",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"core",
|
||||
"framework"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/core"
|
||||
},
|
||||
"time": "2026-05-15T03:31:14+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/database",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/database.git",
|
||||
"reference": "442d12bf0a1edeffc555a59de9f955cb0619c7ca"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/database/zipball/442d12bf0a1edeffc555a59de9f955cb0619c7ca",
|
||||
"reference": "442d12bf0a1edeffc555a59de9f955cb0619c7ca",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/chronos": "^3.3",
|
||||
"cakephp/core": "^5.3.0",
|
||||
"cakephp/datasource": "^5.3.0",
|
||||
"php": ">=8.2",
|
||||
"psr/log": "^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/i18n": "^5.3.0",
|
||||
"cakephp/log": "^5.3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/i18n": "If you are using locale-aware datetime formats.",
|
||||
"cakephp/log": "If you want to use query logging without providing a logger yourself."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Database\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/database/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Flexible and powerful Database abstraction library with a familiar PDO-like API",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"abstraction",
|
||||
"cakephp",
|
||||
"database",
|
||||
"database abstraction",
|
||||
"pdo"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/database"
|
||||
},
|
||||
"time": "2026-05-21T19:38:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/datasource",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/datasource.git",
|
||||
"reference": "b768f0c0bf0fca815f83c4caef14e7fbb2409bf5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/datasource/zipball/b768f0c0bf0fca815f83c4caef14e7fbb2409bf5",
|
||||
"reference": "b768f0c0bf0fca815f83c4caef14e7fbb2409bf5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/core": "^5.3.0",
|
||||
"php": ">=8.2",
|
||||
"psr/simple-cache": "^2.0 || ^3.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cache": "^5.3.0",
|
||||
"cakephp/collection": "^5.3.0",
|
||||
"cakephp/utility": "^5.3.0"
|
||||
},
|
||||
"suggest": {
|
||||
"cakephp/cache": "If you decide to use Query caching.",
|
||||
"cakephp/collection": "If you decide to use ResultSetInterface.",
|
||||
"cakephp/utility": "If you decide to use EntityTrait."
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Cake\\Datasource\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/datasource/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides connection managing and traits for Entities and Queries that can be reused for different datastores",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"connection management",
|
||||
"datasource",
|
||||
"entity",
|
||||
"query"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/datasource"
|
||||
},
|
||||
"time": "2026-05-20T10:27:33+00:00"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/utility",
|
||||
"version": "5.3.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/utility.git",
|
||||
"reference": "4c703a010b9d955fed44731669e35d3043425cc7"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/utility/zipball/4c703a010b9d955fed44731669e35d3043425cc7",
|
||||
"reference": "4c703a010b9d955fed44731669e35d3043425cc7",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/core": "^5.3.0",
|
||||
"php": ">=8.2"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-intl": "To use Text::transliterate() or Text::slug()",
|
||||
"lib-ICU": "To use Text::transliterate() or Text::slug()"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"Cake\\Utility\\": "."
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/utility/graphs/contributors"
|
||||
}
|
||||
],
|
||||
"description": "CakePHP Utility classes such as Inflector, String, Hash, and Security",
|
||||
"homepage": "https://cakephp.org",
|
||||
"keywords": [
|
||||
"cakephp",
|
||||
"hash",
|
||||
"inflector",
|
||||
"security",
|
||||
"string",
|
||||
"utility"
|
||||
],
|
||||
"support": {
|
||||
"forum": "https://stackoverflow.com/tags/cakephp",
|
||||
"irc": "irc://irc.freenode.org/cakephp",
|
||||
"issues": "https://github.com/cakephp/cakephp/issues",
|
||||
"source": "https://github.com/cakephp/utility"
|
||||
},
|
||||
"time": "2026-05-21T19:38:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
"version": "3.2.0",
|
||||
@@ -2627,6 +2951,90 @@
|
||||
],
|
||||
"time": "2022-12-11T20:36:23+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/container",
|
||||
"version": "5.2.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/thephpleague/container.git",
|
||||
"reference": "58accbc032f0090a9bd08326f93062c5a658b2c5"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/thephpleague/container/zipball/58accbc032f0090a9bd08326f93062c5a658b2c5",
|
||||
"reference": "58accbc032f0090a9bd08326f93062c5a658b2c5",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^8.1",
|
||||
"psr/container": "^2.0.2",
|
||||
"psr/event-dispatcher": "^1.0"
|
||||
},
|
||||
"provide": {
|
||||
"psr/container-implementation": "^1.0"
|
||||
},
|
||||
"replace": {
|
||||
"orno/di": "~2.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"nette/php-generator": "^4.1",
|
||||
"nikic/php-parser": "^5.0",
|
||||
"phpstan/phpstan": "^2.1.11",
|
||||
"phpunit/phpunit": "^10.5.45|^11.5.15|^12.0",
|
||||
"roave/security-advisories": "dev-latest",
|
||||
"scrutinizer/ocular": "^1.9",
|
||||
"squizlabs/php_codesniffer": "^3.9"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-1.x": "1.x-dev",
|
||||
"dev-2.x": "2.x-dev",
|
||||
"dev-3.x": "3.x-dev",
|
||||
"dev-4.x": "4.x-dev",
|
||||
"dev-5.x": "5.x-dev",
|
||||
"dev-master": "5.x-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"League\\Container\\": "src"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Phil Bennett",
|
||||
"email": "mail@philbennett.co.uk",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "A fast and intuitive dependency injection container.",
|
||||
"homepage": "https://github.com/thephpleague/container",
|
||||
"keywords": [
|
||||
"container",
|
||||
"dependency",
|
||||
"di",
|
||||
"injection",
|
||||
"league",
|
||||
"provider",
|
||||
"service"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/thephpleague/container/issues",
|
||||
"source": "https://github.com/thephpleague/container/tree/5.2.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/philipobenito",
|
||||
"type": "github"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-19T18:52:39+00:00"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "3.28.0",
|
||||
@@ -4980,6 +5388,93 @@
|
||||
],
|
||||
"time": "2024-04-27T21:32:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "robmorgan/phinx",
|
||||
"version": "0.16.12",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/phinx.git",
|
||||
"reference": "1cce44387a9146dc04c312e6a231b17fb30385cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/phinx/zipball/1cce44387a9146dc04c312e6a231b17fb30385cd",
|
||||
"reference": "1cce44387a9146dc04c312e6a231b17fb30385cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/database": "^5.0.2",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"php-64bit": ">=8.1",
|
||||
"psr/container": "^1.1|^2.0",
|
||||
"symfony/config": "^4.0|^5.0|^6.0|^7.0|^8.0",
|
||||
"symfony/console": "^6.0|^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cakephp-codesniffer": "^5.0",
|
||||
"cakephp/i18n": "^5.0",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"symfony/yaml": "^4.0|^5.0|^6.0|^7.0|^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-json": "Install if using JSON configuration format",
|
||||
"ext-pdo": "PDO extension is needed",
|
||||
"symfony/yaml": "Install if using YAML configuration format"
|
||||
},
|
||||
"bin": [
|
||||
"bin/phinx"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phinx\\": "src/Phinx/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Rob Morgan",
|
||||
"email": "robbym@gmail.com",
|
||||
"homepage": "https://robmorgan.id.au",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"email": "woody.gilk@gmail.com",
|
||||
"homepage": "https://shadowhand.me",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Richard Quadling",
|
||||
"email": "rquadling@gmail.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/phinx/graphs/contributors",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
|
||||
"homepage": "https://phinx.org",
|
||||
"keywords": [
|
||||
"database",
|
||||
"database migrations",
|
||||
"db",
|
||||
"migrations",
|
||||
"phinx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/phinx/issues",
|
||||
"source": "https://github.com/cakephp/phinx/tree/0.16.12"
|
||||
},
|
||||
"time": "2026-07-03T07:27:17+00:00"
|
||||
},
|
||||
{
|
||||
"name": "socialiteproviders/manager",
|
||||
"version": "v4.8.1",
|
||||
@@ -5312,6 +5807,85 @@
|
||||
],
|
||||
"time": "2024-05-31T14:57:53+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v7.4.14",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
|
||||
"reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/filesystem": "^7.1|^8.0",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/finder": "<6.4",
|
||||
"symfony/service-contracts": "<2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/event-dispatcher": "^6.4|^7.0|^8.0",
|
||||
"symfony/finder": "^6.4|^7.0|^8.0",
|
||||
"symfony/messenger": "^6.4|^7.0|^8.0",
|
||||
"symfony/service-contracts": "^2.5|^3",
|
||||
"symfony/yaml": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Config\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/config/tree/v7.4.14"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-06-09T07:51:57+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.1.3",
|
||||
@@ -5768,6 +6342,76 @@
|
||||
],
|
||||
"time": "2024-04-18T09:32:20+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v7.4.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/filesystem.git",
|
||||
"reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/filesystem/zipball/d721ea61b4a5fba8c5b6e7c1feda19efea144b50",
|
||||
"reference": "d721ea61b4a5fba8c5b6e7c1feda19efea144b50",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/polyfill-ctype": "~1.8",
|
||||
"symfony/polyfill-mbstring": "~1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/process": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Filesystem\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Provides basic utilities for the filesystem",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/filesystem/tree/v7.4.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2026-05-11T16:38:44+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v7.1.3",
|
||||
@@ -11355,6 +11999,6 @@
|
||||
"php": "^8.2.0",
|
||||
"ext-json": "*"
|
||||
},
|
||||
"platform-dev": [],
|
||||
"platform-dev": {},
|
||||
"plugin-api-version": "2.6.0"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreatePhinxTestTable extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('phinx_test');
|
||||
$table
|
||||
->addColumn('name', 'string', ['limit' => 100, 'null' => false])
|
||||
->addColumn('created_at', 'datetime', ['default' => 'CURRENT_TIMESTAMP'])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddAddnoteToTemplateMapping extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('template_mapping');
|
||||
$table->addColumn('is_addnote', 'boolean', [
|
||||
'default' => 0,
|
||||
'null' => false,
|
||||
'after' => 'is_visible_parts',
|
||||
'comment' => 'Campo utilizzabile come addnote (multi-selezione)',
|
||||
])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreateOptionsTable extends AbstractMigration
|
||||
{
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('options');
|
||||
$table
|
||||
->addColumn('opt_key', 'string', ['limit' => 100])
|
||||
->addColumn('opt_value', 'text', ['null' => true])
|
||||
->addColumn('created_at', 'timestamp', ['default' => 'CURRENT_TIMESTAMP'])
|
||||
->addColumn('updated_at', 'timestamp', [
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
'update' => 'CURRENT_TIMESTAMP',
|
||||
])
|
||||
->addIndex(['opt_key'], ['unique' => true])
|
||||
->create();
|
||||
|
||||
// Righe iniziali del popup
|
||||
$this->table('options')->insert([
|
||||
[
|
||||
'opt_key' => 'popup_enabled',
|
||||
'opt_value' => '1',
|
||||
],
|
||||
[
|
||||
'opt_key' => 'popup_version',
|
||||
'opt_value' => '2026-07-migrazione',
|
||||
],
|
||||
[
|
||||
'opt_key' => 'popup',
|
||||
'opt_value' =>
|
||||
'<h2 style="margin-top:0;">Nuovo portale disponibile</h2>'
|
||||
. '<p>Ci siamo spostati. Da ora usa il nuovo indirizzo:</p>'
|
||||
. '<p style="text-align:center;">'
|
||||
. '<a href="https://trfsmart-bv.cesoft.io/" '
|
||||
. 'style="display:inline-block;padding:12px 24px;background:#2563eb;'
|
||||
. 'color:#fff;text-decoration:none;border-radius:6px;">Vai al nuovo portale</a>'
|
||||
. '</p>'
|
||||
. '<p style="font-size:13px;color:#666;">Salva il nuovo indirizzo nei preferiti. '
|
||||
. 'Se riscontri problemi puoi continuare a usare questo portale per ora.</p>',
|
||||
],
|
||||
])->saveData();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class AddExportDateToDatadb extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Aggiunge export_date su datadb: data/ora di esportazione verso il LIMS,
|
||||
* valorizzata insieme al numero commessa quando status passa a 'l'.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$this->table('datadb')
|
||||
->addColumn('export_date', 'datetime', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'after' => 'commessaweb',
|
||||
'comment' => 'Data/ora esportazione verso LIMS (valorizzata quando status = l)',
|
||||
])
|
||||
->addIndex(['export_date'], ['name' => 'idx_datadb_export_date'])
|
||||
->update();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use Phinx\Migration\AbstractMigration;
|
||||
|
||||
final class CreateLimsDailySamplesTable extends AbstractMigration
|
||||
{
|
||||
/**
|
||||
* Tabella con un dato al giorno: numero totale di campioni inseriti
|
||||
* nel LIMS (loro software) in quella giornata. Serve per calcolare
|
||||
* la % di campioni esportati sul totale inserito nel LIMS.
|
||||
*/
|
||||
public function change(): void
|
||||
{
|
||||
$table = $this->table('lims_daily_samples', [
|
||||
'id' => false,
|
||||
'primary_key' => ['id'],
|
||||
'comment' => 'Totale campioni inseriti nel LIMS per giornata',
|
||||
]);
|
||||
|
||||
$table
|
||||
->addColumn('id', 'integer', [
|
||||
'identity' => true,
|
||||
'signed' => false,
|
||||
])
|
||||
->addColumn('sample_date', 'date', [
|
||||
'null' => false,
|
||||
'comment' => 'Giornata di riferimento',
|
||||
])
|
||||
->addColumn('total_samples', 'integer', [
|
||||
'signed' => false,
|
||||
'null' => false,
|
||||
'default' => 0,
|
||||
'comment' => 'Totale campioni inseriti nel LIMS in quella giornata',
|
||||
])
|
||||
->addColumn('note', 'string', [
|
||||
'limit' => 255,
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
])
|
||||
->addColumn('created_at', 'timestamp', [
|
||||
'null' => true,
|
||||
'default' => 'CURRENT_TIMESTAMP',
|
||||
])
|
||||
->addColumn('updated_at', 'timestamp', [
|
||||
'null' => true,
|
||||
'default' => null,
|
||||
'update' => 'CURRENT_TIMESTAMP',
|
||||
])
|
||||
->addIndex(['sample_date'], [
|
||||
'unique' => true,
|
||||
'name' => 'uq_lims_daily_samples_date',
|
||||
])
|
||||
->create();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
require_once __DIR__ . '/vendor/autoload.php';
|
||||
|
||||
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
|
||||
$dotenv->safeLoad();
|
||||
|
||||
return [
|
||||
'paths' => [
|
||||
'migrations' => '%%PHINX_CONFIG_DIR%%/db/migrations',
|
||||
'seeds' => '%%PHINX_CONFIG_DIR%%/db/seeds',
|
||||
],
|
||||
'environments' => [
|
||||
'default_migration_table' => 'phinxlog',
|
||||
'default_environment' => 'production',
|
||||
'production' => [
|
||||
'adapter' => 'mysql',
|
||||
'host' => $_ENV['DB_HOST'] ?? 'localhost',
|
||||
'name' => $_ENV['DB_DATABASE'] ?? '',
|
||||
'user' => $_ENV['DB_USERNAME'] ?? '',
|
||||
'pass' => $_ENV['DB_PASSWORD'] ?? '',
|
||||
'port' => $_ENV['DB_PORT'] ?? 3306,
|
||||
'charset' => 'utf8mb4',
|
||||
],
|
||||
],
|
||||
'version_order' => 'creation',
|
||||
];
|
||||
@@ -19,6 +19,9 @@ $(document).ready(function () {
|
||||
let descriptionTextbox = null;
|
||||
let markerObjects = {};
|
||||
let nextMarkerId = 1;
|
||||
let freeTextMode = false;
|
||||
let freeTextObjects = {};
|
||||
let nextFreeTextId = 1;
|
||||
let partsListData = [];
|
||||
// DIMENSIONE GLOBALE MARKER
|
||||
let globalMarkerSize = 16;
|
||||
@@ -55,7 +58,7 @@ $(document).ready(function () {
|
||||
.data("iddatadb", iddatadb || null)
|
||||
.data("idquotations", idquotations || null);
|
||||
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
// Parti (non legate al canvas) possono partire subito
|
||||
loadExistingParts(iddatadb, idquotations);
|
||||
|
||||
const modalElement = document.getElementById("annotationsModal");
|
||||
@@ -74,6 +77,18 @@ $(document).ready(function () {
|
||||
focus: true,
|
||||
});
|
||||
}
|
||||
|
||||
// IMPORTANTE: carica la foto SOLO dopo che il modale è completamente
|
||||
// visibile (animazione fade terminata). Se l'immagine è già in cache
|
||||
// del browser, l'evento "load" può scattare quasi subito, PRIMA che
|
||||
// il contenitore del canvas abbia dimensioni reali (parent ancora
|
||||
// display:none durante il fade) — questo rompeva il canvas Fabric.js
|
||||
// per la prima foto mostrata (bug riprodotto solo con foto già in cache,
|
||||
// tipico del server di test dove le foto erano già state visualizzate).
|
||||
$(modalElement).one("shown.bs.modal", function () {
|
||||
loadPhoto(iddatadb, idquotations);
|
||||
});
|
||||
|
||||
modal.show();
|
||||
// Inizializza slider dimensione marker
|
||||
$("#markerSizeSlider").val(globalMarkerSize);
|
||||
@@ -132,6 +147,9 @@ $(document).ready(function () {
|
||||
selectedPartNumber = null;
|
||||
unsavedChanges = false;
|
||||
partsListData = [];
|
||||
freeTextMode = false; // ← aggiungi
|
||||
freeTextObjects = {}; // ← aggiungi
|
||||
$("#addFreeTextBtnAnnotations").removeClass("active"); // ← aggiungi
|
||||
if (fabricCanvas) {
|
||||
fabricCanvas.off();
|
||||
fabricCanvas.dispose();
|
||||
@@ -180,6 +198,29 @@ $(document).ready(function () {
|
||||
updateMarkers();
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
// ===================
|
||||
// TESTO LIBERO — TOGGLE MODALITÀ
|
||||
// ===================
|
||||
$(document)
|
||||
.off("click.freeTextToggle", "#addFreeTextBtnAnnotations")
|
||||
.on("click.freeTextToggle", "#addFreeTextBtnAnnotations", function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
freeTextMode = !freeTextMode;
|
||||
$(this).toggleClass("active", freeTextMode);
|
||||
|
||||
// Disattiva la selezione parte/marker mentre si aggiunge testo libero
|
||||
if (freeTextMode) {
|
||||
selectedPartNumber = null;
|
||||
$("#partsListAnnotations .list-group-item").removeClass(
|
||||
"active",
|
||||
);
|
||||
}
|
||||
|
||||
console.log("Free text mode:", freeTextMode);
|
||||
});
|
||||
// ===================
|
||||
// COLORE LISTA DESCRIZIONI
|
||||
// ===================
|
||||
@@ -193,9 +234,22 @@ $(document).ready(function () {
|
||||
|
||||
if (descriptionTextbox && fabricCanvas) {
|
||||
descriptionTextbox.set("fill", globalDescriptionColor);
|
||||
fabricCanvas.renderAll();
|
||||
}
|
||||
|
||||
// Aggiorna anche i testi liberi già piazzati sulla foto corrente
|
||||
const currentPhoto = $("#samplePhotoAnnotations").attr("src");
|
||||
const annotations = photoAnnotations[currentPhoto];
|
||||
if (annotations && annotations.freeTexts) {
|
||||
annotations.freeTexts.forEach((entry) => {
|
||||
entry.color = globalDescriptionColor;
|
||||
});
|
||||
}
|
||||
Object.values(freeTextObjects).forEach((obj) => {
|
||||
obj.set("fill", globalDescriptionColor);
|
||||
});
|
||||
|
||||
if (fabricCanvas) fabricCanvas.renderAll();
|
||||
|
||||
markUnsaved();
|
||||
},
|
||||
);
|
||||
@@ -370,7 +424,29 @@ $(document).ready(function () {
|
||||
console.log(
|
||||
"Evento mouse:down su canvas, selectedPartNumber:",
|
||||
selectedPartNumber,
|
||||
"freeTextMode:",
|
||||
freeTextMode,
|
||||
);
|
||||
|
||||
// Modalità testo libero: ha priorità, un solo click e si disattiva da sola
|
||||
if (freeTextMode) {
|
||||
if (options.target) {
|
||||
console.log(
|
||||
"Click su un oggetto esistente, ignoro.",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const pointer = fabricCanvas.getPointer(options.e);
|
||||
const x = pointer.x / photoData.scale;
|
||||
const y = pointer.y / photoData.scale;
|
||||
|
||||
addFreeText(x, y);
|
||||
|
||||
freeTextMode = false;
|
||||
$("#addFreeTextBtnAnnotations").removeClass("active");
|
||||
return;
|
||||
}
|
||||
|
||||
if (selectedPartNumber === null) {
|
||||
console.log(
|
||||
"Nessuna parte selezionata, ignoro il click.",
|
||||
@@ -425,6 +501,7 @@ $(document).ready(function () {
|
||||
|
||||
updateMarkers();
|
||||
updateDescriptions();
|
||||
updateFreeTexts();
|
||||
}, 10);
|
||||
});
|
||||
}
|
||||
@@ -985,7 +1062,128 @@ $(document).ready(function () {
|
||||
},
|
||||
});
|
||||
}
|
||||
// ===================
|
||||
// TESTO LIBERO — CREAZIONE E RENDER
|
||||
// ===================
|
||||
function addFreeText(x, y) {
|
||||
const currentPhoto = $("#samplePhotoAnnotations").attr("src");
|
||||
|
||||
if (!photoAnnotations[currentPhoto]) {
|
||||
photoAnnotations[currentPhoto] = {
|
||||
markers: [],
|
||||
freeTexts: [],
|
||||
hasDescriptions: false,
|
||||
descriptionPosition: { x: 10, y: 10 },
|
||||
descriptionSize: {
|
||||
width: photoData.displayWidth * 0.3,
|
||||
height: photoData.displayHeight * 0.3,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (!photoAnnotations[currentPhoto].freeTexts) {
|
||||
photoAnnotations[currentPhoto].freeTexts = [];
|
||||
}
|
||||
|
||||
const entry = {
|
||||
id: nextFreeTextId++,
|
||||
x,
|
||||
y,
|
||||
text: "Testo",
|
||||
color: globalDescriptionColor,
|
||||
fontSize: Math.max(16, Math.round(globalMarkerSize * 0.8)),
|
||||
};
|
||||
|
||||
photoAnnotations[currentPhoto].freeTexts.push(entry);
|
||||
renderFreeText(entry, true);
|
||||
markUnsaved();
|
||||
}
|
||||
|
||||
function renderFreeText(entry, startEditing = false) {
|
||||
const itext = new fabric.IText(entry.text, {
|
||||
left: entry.x * photoData.scale,
|
||||
top: entry.y * photoData.scale,
|
||||
fontFamily: "Arial",
|
||||
fontSize: entry.fontSize,
|
||||
fill: entry.color,
|
||||
selectable: true,
|
||||
hasControls: true,
|
||||
editable: true,
|
||||
borderColor: "#333",
|
||||
cornerColor: "#000",
|
||||
cornerSize: 12,
|
||||
transparentCorners: false,
|
||||
cornerStyle: "rect",
|
||||
borderScaleFactor: 2,
|
||||
});
|
||||
|
||||
itext.freeTextId = entry.id;
|
||||
// FIX: Fabric.js appende la <textarea> nascosta (usata per catturare la
|
||||
// tastiera durante l'editing) a document.body, FUORI dal modale.
|
||||
// Il focus trap di Bootstrap la considera "esterna" e ruba il focus
|
||||
// ogni volta che Fabric lo assegna a quella textarea — così i tasti
|
||||
// digitati non arrivano mai al testo. Spostiamo la textarea dentro
|
||||
// il modale non appena l'editing viene avviato.
|
||||
itext.on("editing:entered", function () {
|
||||
const modalEl = document.getElementById("annotationsModal");
|
||||
if (
|
||||
this.hiddenTextarea &&
|
||||
modalEl &&
|
||||
this.hiddenTextarea.parentNode !== modalEl
|
||||
) {
|
||||
modalEl.appendChild(this.hiddenTextarea);
|
||||
}
|
||||
});
|
||||
itext.on("moving", function () {
|
||||
entry.x = this.left / photoData.scale;
|
||||
entry.y = this.top / photoData.scale;
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
itext.on("editing:exited", function () {
|
||||
entry.text = this.text;
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
itext.on("scaling", function () {
|
||||
entry.fontSize = Math.round(this.fontSize * this.scaleY);
|
||||
this.set({ fontSize: entry.fontSize, scaleX: 1, scaleY: 1 });
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
fabricCanvas.add(itext);
|
||||
freeTextObjects[entry.id] = itext;
|
||||
|
||||
// IMPORTANTE: non attivare l'editing nello stesso evento mouse:down che
|
||||
// ha creato l'oggetto. Fabric processa quel click come "click su canvas
|
||||
// vuoto" (il target non esisteva ancora al momento del mousedown) e al
|
||||
// successivo mouseup deseleziona tutto, interrompendo l'editing appena
|
||||
// avviato — l'utente non riesce a scrivere né a spostare il testo.
|
||||
// Rimandiamo l'attivazione al prossimo giro di event loop, così avviene
|
||||
// DOPO che Fabric ha concluso la gestione nativa di quel click.
|
||||
if (startEditing) {
|
||||
setTimeout(() => {
|
||||
fabricCanvas.setActiveObject(itext);
|
||||
itext.enterEditing();
|
||||
itext.selectAll();
|
||||
fabricCanvas.renderAll();
|
||||
}, 0);
|
||||
}
|
||||
|
||||
fabricCanvas.renderAll();
|
||||
}
|
||||
function updateFreeTexts() {
|
||||
for (let id in freeTextObjects) {
|
||||
fabricCanvas.remove(freeTextObjects[id]);
|
||||
delete freeTextObjects[id];
|
||||
}
|
||||
freeTextObjects = {};
|
||||
|
||||
const currentPhoto = $("#samplePhotoAnnotations").attr("src");
|
||||
const annotations = photoAnnotations[currentPhoto];
|
||||
if (!annotations || !annotations.freeTexts) return;
|
||||
|
||||
annotations.freeTexts.forEach((entry) => renderFreeText(entry, false));
|
||||
}
|
||||
// ===================
|
||||
// MARKERS & DESCRIPTIONS
|
||||
// ===================
|
||||
@@ -1291,6 +1489,40 @@ $(document).ready(function () {
|
||||
}
|
||||
}
|
||||
|
||||
// ===================
|
||||
// TESTO LIBERO — RIMOZIONE CON CANC/DELETE
|
||||
// ===================
|
||||
$(document).on("keydown.freeTextDelete", function (e) {
|
||||
// Solo se il modale annotazioni è aperto
|
||||
if (!$("#annotationsModal").hasClass("show")) return;
|
||||
if (!fabricCanvas) return;
|
||||
|
||||
// Ignora se si sta scrivendo dentro un campo di editing testo
|
||||
// (es. l'utente sta modificando il contenuto dell'IText stesso)
|
||||
const active = fabricCanvas.getActiveObject();
|
||||
if (active && active.isEditing) return;
|
||||
|
||||
if (e.key !== "Delete" && e.key !== "Backspace") return;
|
||||
if (!active || active.freeTextId === undefined) return;
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
const currentPhoto = $("#samplePhotoAnnotations").attr("src");
|
||||
const annotations = photoAnnotations[currentPhoto];
|
||||
|
||||
if (annotations && annotations.freeTexts) {
|
||||
annotations.freeTexts = annotations.freeTexts.filter(
|
||||
(entry) => entry.id !== active.freeTextId,
|
||||
);
|
||||
}
|
||||
|
||||
fabricCanvas.remove(active);
|
||||
delete freeTextObjects[active.freeTextId];
|
||||
fabricCanvas.renderAll();
|
||||
|
||||
markUnsaved();
|
||||
});
|
||||
|
||||
// Delegazione evento per il pulsante "Descrizioni"
|
||||
$(document)
|
||||
.off("click.addDescriptions", "#addDescriptionsBtnAnnotations")
|
||||
|
||||
@@ -1,449 +0,0 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
require_once(__DIR__ . '/class/binding-functions.php');
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
// Filtri comuni.
|
||||
$templateFilter = isset($_GET['template_id']) ? intval($_GET['template_id']) : 0;
|
||||
$search = trim($_GET['q'] ?? '');
|
||||
$perPage = 50;
|
||||
$page = max(1, intval($_GET['page'] ?? 1));
|
||||
$dir = (strtolower($_GET['dir'] ?? 'asc') === 'desc') ? 'DESC' : 'ASC';
|
||||
|
||||
// Modalita': overview (gruppi per campo) oppure detail (valori di un campo).
|
||||
$focusTarget = trim($_GET['target'] ?? '');
|
||||
$mode = $focusTarget !== '' ? 'detail' : 'overview';
|
||||
|
||||
$templates = $pdo->query("SELECT id, name FROM excel_templates ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Helper: URL che preserva i filtri correnti.
|
||||
$bmUrl = function (array $ov) use ($templateFilter, $search, $dir, $page, $focusTarget) {
|
||||
$base = [
|
||||
'template_id' => $templateFilter ?: null,
|
||||
'q' => $search !== '' ? $search : null,
|
||||
'order' => $_GET['order'] ?? null,
|
||||
'dir' => strtolower($dir),
|
||||
'page' => $page,
|
||||
'target' => $focusTarget !== '' ? $focusTarget : null,
|
||||
];
|
||||
$p = array_filter(array_merge($base, $ov), fn($v) => $v !== null && $v !== '');
|
||||
return 'bindings_manage.php?' . http_build_query($p);
|
||||
};
|
||||
|
||||
// Filtri WHERE comuni (template/kind/search).
|
||||
$conds = [];
|
||||
$params = [];
|
||||
if ($templateFilter > 0) {
|
||||
$conds[] = 'b.template_id = ?';
|
||||
$params[] = $templateFilter;
|
||||
}
|
||||
|
||||
if ($mode === 'detail') {
|
||||
// ---- DETAIL: valori (json_value -> lims) di un singolo campo (target_key) ----
|
||||
$conds[] = 'b.target_key = ?';
|
||||
$params[] = $focusTarget;
|
||||
if ($search !== '') {
|
||||
$conds[] = '(b.json_value LIKE ? OR b.lims_value LIKE ?)';
|
||||
$like = '%' . $search . '%';
|
||||
array_push($params, $like, $like);
|
||||
}
|
||||
$where = 'WHERE ' . implode(' AND ', $conds);
|
||||
|
||||
$orderCols = ['json' => 'b.json_value', 'lims' => 'b.lims_value'];
|
||||
$order = array_key_exists($_GET['order'] ?? '', $orderCols) ? $_GET['order'] : 'json';
|
||||
$orderSql = $orderCols[$order] . ' ' . $dir;
|
||||
|
||||
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM json_lims_binding b $where");
|
||||
$countStmt->execute($params);
|
||||
$total = (int) $countStmt->fetchColumn();
|
||||
$totalPages = max(1, (int) ceil($total / $perPage));
|
||||
if ($page > $totalPages) $page = $totalPages;
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$sql = "SELECT b.id, b.template_id, b.binding_kind, b.mapping_id, b.fixed_field_key, b.field_id,
|
||||
b.json_value, b.lims_value_id, b.lims_value,
|
||||
t.name AS template_name, m.field_label
|
||||
FROM json_lims_binding b
|
||||
LEFT JOIN excel_templates t ON t.id = b.template_id
|
||||
LEFT JOIN template_mapping m ON m.id = b.mapping_id
|
||||
$where
|
||||
ORDER BY $orderSql, b.json_value ASC
|
||||
LIMIT $perPage OFFSET $offset";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Intestazione del gruppo (tutte le righe condividono campo/template/kind).
|
||||
$head = $rows[0] ?? null;
|
||||
if (!$head) {
|
||||
// Target vuoto (es. dopo aver svuotato il campo): ricavo i meta dal target_key.
|
||||
$head = ['binding_kind' => str_starts_with($focusTarget, 'fx:') ? 'fixed' : 'custom'];
|
||||
}
|
||||
$headKind = $head['binding_kind'] ?? 'custom';
|
||||
if ($headKind === 'fixed') {
|
||||
$headFixedKey = $head['fixed_field_key'] ?? (explode(':', $focusTarget)[2] ?? '');
|
||||
$headLabel = binding_fixed_label((string) $headFixedKey);
|
||||
$headTpl = (int) ($head['template_id'] ?? (explode(':', $focusTarget)[1] ?? 0));
|
||||
} else {
|
||||
$headLabel = $head['field_label'] ?? ('mapping ' . ($head['mapping_id'] ?? ''));
|
||||
$headTpl = (int) ($head['template_id'] ?? 0);
|
||||
}
|
||||
$headTplName = $headTpl ? ($pdo->query("SELECT name FROM excel_templates WHERE id=" . $headTpl)->fetchColumn() ?: ('#' . $headTpl)) : '';
|
||||
} else {
|
||||
// ---- OVERVIEW: un gruppo per campo (target_key) con il conteggio ----
|
||||
if ($search !== '') {
|
||||
$conds[] = '(t.name LIKE ? OR m.field_label LIKE ? OR b.fixed_field_key LIKE ?)';
|
||||
$like = '%' . $search . '%';
|
||||
array_push($params, $like, $like, $like);
|
||||
}
|
||||
$where = $conds ? ('WHERE ' . implode(' AND ', $conds)) : '';
|
||||
|
||||
$orderCols = ['template' => 'template_name', 'field' => 'field_label', 'count' => 'cnt'];
|
||||
$order = array_key_exists($_GET['order'] ?? '', $orderCols) ? $_GET['order'] : 'template';
|
||||
$orderSql = $orderCols[$order] . ' ' . $dir;
|
||||
|
||||
$countStmt = $pdo->prepare("SELECT COUNT(DISTINCT b.target_key)
|
||||
FROM json_lims_binding b
|
||||
LEFT JOIN excel_templates t ON t.id = b.template_id
|
||||
LEFT JOIN template_mapping m ON m.id = b.mapping_id $where");
|
||||
$countStmt->execute($params);
|
||||
$total = (int) $countStmt->fetchColumn();
|
||||
$totalPages = max(1, (int) ceil($total / $perPage));
|
||||
if ($page > $totalPages) $page = $totalPages;
|
||||
$offset = ($page - 1) * $perPage;
|
||||
|
||||
$sql = "SELECT b.target_key, b.template_id, b.binding_kind, b.mapping_id, b.fixed_field_key,
|
||||
MAX(t.name) AS template_name, MAX(m.field_label) AS field_label,
|
||||
COUNT(*) AS cnt, MAX(b.updated_at) AS last_updated
|
||||
FROM json_lims_binding b
|
||||
LEFT JOIN excel_templates t ON t.id = b.template_id
|
||||
LEFT JOIN template_mapping m ON m.id = b.mapping_id
|
||||
$where
|
||||
GROUP BY b.target_key, b.template_id, b.binding_kind, b.mapping_id, b.fixed_field_key
|
||||
ORDER BY $orderSql, template_name ASC
|
||||
LIMIT $perPage OFFSET $offset";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
$groups = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
$sortLink = function (string $col, string $label) use ($bmUrl, $order, $dir) {
|
||||
$nextDir = ($order === $col && $dir === 'ASC') ? 'desc' : 'asc';
|
||||
$caret = $order === $col ? ($dir === 'ASC' ? ' ▲' : ' ▼') : '';
|
||||
return '<a href="' . htmlspecialchars($bmUrl(['order' => $col, 'dir' => $nextDir, 'page' => 1]))
|
||||
. '" class="text-decoration-none text-reset">' . $label . $caret . '</a>';
|
||||
};
|
||||
?>
|
||||
<!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/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.json-value {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
td .select2-container {
|
||||
min-width: 220px;
|
||||
}
|
||||
|
||||
.row-status {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.group-row {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.group-row:hover {
|
||||
background-color: #f1f5ff;
|
||||
}
|
||||
</style>
|
||||
<title>Gestione Binding JSON → LIMS - <?= 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 radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<h6 class="mb-0">Gestione Binding JSON → LIMS</h6>
|
||||
<form method="GET" class="d-flex align-items-center gap-2 flex-wrap mb-0">
|
||||
<?php if ($mode === 'detail'): ?>
|
||||
<input type="hidden" name="target" value="<?= htmlspecialchars($focusTarget) ?>">
|
||||
<?php endif; ?>
|
||||
<input type="hidden" name="dir" value="<?= htmlspecialchars(strtolower($dir)) ?>">
|
||||
<input type="text" name="q" class="form-control form-control-sm" style="width:220px;"
|
||||
placeholder="<?= $mode === 'detail' ? 'Cerca valore...' : 'Cerca campo/template...' ?>"
|
||||
value="<?= htmlspecialchars($search) ?>">
|
||||
<?php if ($mode === 'overview'): ?>
|
||||
<select name="template_id" class="form-select form-select-sm" style="width:auto;" onchange="this.form.submit()">
|
||||
<option value="0">Tutti i template</option>
|
||||
<?php foreach ($templates as $t): ?>
|
||||
<option value="<?= (int) $t['id'] ?>" <?= $templateFilter === (int) $t['id'] ? 'selected' : '' ?>>
|
||||
<?= htmlspecialchars($t['name']) ?>
|
||||
</option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
<?php endif; ?>
|
||||
<button type="submit" class="btn btn-sm btn-primary">Cerca</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div id="globalError" class="alert alert-danger" style="display:none;"></div>
|
||||
|
||||
<?php if ($mode === 'detail'): ?>
|
||||
<div class="d-flex justify-content-between align-items-center mb-2 flex-wrap gap-2">
|
||||
<div>
|
||||
<a href="<?= htmlspecialchars($bmUrl(['target' => null, 'page' => 1, 'order' => null])) ?>" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-arrow-left"></i> Tutti i campi
|
||||
</a>
|
||||
<span class="ms-2">
|
||||
<strong><?= htmlspecialchars($headLabel) ?></strong>
|
||||
<?php if ($headKind === 'fixed'): ?><span class="badge bg-light text-dark border">fixed</span><?php endif; ?>
|
||||
<span class="text-muted">· <?= htmlspecialchars((string) $headTplName) ?></span>
|
||||
</span>
|
||||
</div>
|
||||
<span class="small text-muted"><?= $total ?> valore/i · pagina <?= $page ?>/<?= $totalPages ?></span>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered align-middle" id="bindingsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= $sortLink('json', 'Valore JSON') ?></th>
|
||||
<th><?= $sortLink('lims', 'Valore LIMS') ?></th>
|
||||
<th style="width:170px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($rows)): ?>
|
||||
<tr class="no-data-row">
|
||||
<td colspan="3" class="text-center text-muted">Nessun valore per questo campo.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($rows as $b): ?>
|
||||
<?php $bDangling = ($b['binding_kind'] === 'custom' && $b['field_label'] === null); ?>
|
||||
<tr data-id="<?= (int) $b['id'] ?>"
|
||||
data-kind="<?= $b['binding_kind'] ?>"
|
||||
data-mapping-id="<?= (int) $b['mapping_id'] ?>"
|
||||
data-field-id="<?= (int) $b['field_id'] ?>"
|
||||
data-fixed-key="<?= htmlspecialchars((string) $b['fixed_field_key'], ENT_QUOTES) ?>"
|
||||
data-template-id="<?= (int) $b['template_id'] ?>"
|
||||
data-json-value="<?= htmlspecialchars($b['json_value'], ENT_QUOTES) ?>">
|
||||
<td class="json-value"><?= htmlspecialchars($b['json_value']) ?></td>
|
||||
<td>
|
||||
<select class="form-select binding-select" <?= $bDangling ? 'disabled' : '' ?>>
|
||||
<?php if ($b['lims_value_id']): ?>
|
||||
<option value="<?= (int) $b['lims_value_id'] ?>" selected><?= htmlspecialchars($b['lims_value']) ?></option>
|
||||
<?php endif; ?>
|
||||
</select>
|
||||
<span class="row-status text-muted"></span>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-success save-binding-btn" disabled>
|
||||
<i class="fas fa-save"></i> Salva
|
||||
</button>
|
||||
<button type="button" class="btn btn-sm btn-outline-danger delete-binding-btn">
|
||||
<i class="fas fa-trash"></i>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<?php else: ?>
|
||||
<div class="alert alert-secondary small">
|
||||
Scegli un campo per gestirne i valori. Le modifiche valgono per le <strong>importazioni future</strong>.
|
||||
</div>
|
||||
<div class="small text-muted mb-2"><?= $total ?> camp<?= $total === 1 ? 'o' : 'i' ?> con binding · pagina <?= $page ?>/<?= $totalPages ?></div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-bordered align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th><?= $sortLink('template', 'Template') ?></th>
|
||||
<th><?= $sortLink('field', 'Campo') ?></th>
|
||||
<th style="width:120px;"><?= $sortLink('count', '# Binding') ?></th>
|
||||
<th style="width:90px;"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($groups)): ?>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center text-muted">Nessun binding presente.</td>
|
||||
</tr>
|
||||
<?php else: ?>
|
||||
<?php foreach ($groups as $g): ?>
|
||||
<?php
|
||||
$gKind = $g['binding_kind'];
|
||||
$gDangling = ($gKind === 'custom' && $g['field_label'] === null);
|
||||
$gLabel = $gKind === 'fixed'
|
||||
? binding_fixed_label((string) $g['fixed_field_key'])
|
||||
: ($g['field_label'] ?? ('mapping ' . $g['mapping_id']));
|
||||
$gUrl = $bmUrl(['target' => $g['target_key'], 'page' => 1, 'order' => null, 'q' => null]);
|
||||
?>
|
||||
<tr class="group-row" onclick="window.location.href='<?= htmlspecialchars($gUrl) ?>'">
|
||||
<td><?= htmlspecialchars($g['template_name'] ?? ('#' . $g['template_id'])) ?></td>
|
||||
<td>
|
||||
<?= htmlspecialchars($gLabel) ?>
|
||||
<?php if ($gKind === 'fixed'): ?><span class="badge bg-light text-dark border">fixed</span><?php endif; ?>
|
||||
<?php if ($gDangling): ?><span class="badge bg-warning text-dark" title="Il campo mappato non esiste piu'">campo rimosso</span><?php endif; ?>
|
||||
</td>
|
||||
<td><span class="badge bg-primary"><?= (int) $g['cnt'] ?></span></td>
|
||||
<td><a href="<?= htmlspecialchars($gUrl) ?>" class="btn btn-sm btn-outline-primary">Apri</a></td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($totalPages > 1): ?>
|
||||
<nav class="d-flex justify-content-center mt-2">
|
||||
<ul class="pagination pagination-sm mb-0">
|
||||
<li class="page-item <?= $page <= 1 ? 'disabled' : '' ?>">
|
||||
<a class="page-link" href="<?= htmlspecialchars($bmUrl(['page' => max(1, $page - 1)])) ?>">«</a>
|
||||
</li>
|
||||
<li class="page-item disabled"><span class="page-link"><?= $page ?> / <?= $totalPages ?></span></li>
|
||||
<li class="page-item <?= $page >= $totalPages ? 'disabled' : '' ?>">
|
||||
<a class="page-link" href="<?= htmlspecialchars($bmUrl(['page' => min($totalPages, $page + 1)])) ?>">»</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
<?php endif; ?>
|
||||
</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>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
const $globalError = $('#globalError');
|
||||
|
||||
// Select2 solo nelle righe valore della pagina corrente (max 50), saltando le disabilitate.
|
||||
$('.binding-select').not(':disabled').each(function() {
|
||||
const $row = $(this).closest('tr');
|
||||
const isFixed = $row.data('kind') === 'fixed';
|
||||
const initialVal = $(this).val();
|
||||
|
||||
$(this).select2({
|
||||
width: '220px',
|
||||
ajax: {
|
||||
url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
|
||||
dataType: 'json',
|
||||
delay: 200,
|
||||
data: params => isFixed ? {
|
||||
field_key: $row.data('fixed-key'),
|
||||
template_id: $row.data('template-id'),
|
||||
q: params.term || '',
|
||||
limit: 50
|
||||
} : {
|
||||
field_id: $row.data('field-id'),
|
||||
q: params.term || '',
|
||||
limit: 50
|
||||
},
|
||||
processResults: data => ({
|
||||
results: data.results || []
|
||||
})
|
||||
},
|
||||
minimumInputLength: 0
|
||||
});
|
||||
|
||||
$(this).data('original-val', initialVal);
|
||||
});
|
||||
|
||||
$('.binding-select').on('change', function() {
|
||||
const $row = $(this).closest('tr');
|
||||
const changed = String($(this).val()) !== String($(this).data('original-val'));
|
||||
$row.find('.save-binding-btn').prop('disabled', !changed);
|
||||
});
|
||||
|
||||
$('.save-binding-btn').on('click', function() {
|
||||
const $row = $(this).closest('tr');
|
||||
const $select = $row.find('.binding-select');
|
||||
const selectedData = $select.select2('data')[0] || {};
|
||||
const $status = $row.find('.row-status');
|
||||
const $btn = $(this);
|
||||
|
||||
$globalError.hide();
|
||||
$btn.prop('disabled', true);
|
||||
$status.text('Salvataggio...').removeClass('text-success text-danger').addClass('text-muted');
|
||||
|
||||
const isFixed = $row.data('kind') === 'fixed';
|
||||
const targetFields = isFixed
|
||||
? { kind: 'fixed', fixed_field_key: $row.data('fixed-key') }
|
||||
: { kind: 'custom', mapping_id: $row.data('mapping-id'), field_id: $row.data('field-id') };
|
||||
|
||||
$.post('save_binding.php', {
|
||||
...targetFields,
|
||||
template_id: $row.data('template-id'),
|
||||
json_value: String($row.data('json-value')),
|
||||
lims_value_id: $select.val(),
|
||||
lims_value: selectedData.text || ''
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$status.text('Salvato').removeClass('text-muted').addClass('text-success');
|
||||
$select.data('original-val', $select.val());
|
||||
} else {
|
||||
throw new Error((resp && resp.error) || 'Errore salvataggio');
|
||||
}
|
||||
}).catch(err => {
|
||||
$status.text('Errore').removeClass('text-muted').addClass('text-danger');
|
||||
$globalError.text(err.message || 'Errore durante il salvataggio.').show();
|
||||
$btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
|
||||
$('.delete-binding-btn').on('click', function() {
|
||||
if (!confirm('Eliminare questo binding?')) return;
|
||||
const $row = $(this).closest('tr');
|
||||
const $btn = $(this);
|
||||
$globalError.hide();
|
||||
$btn.prop('disabled', true);
|
||||
|
||||
$.post('delete_binding.php', {
|
||||
id: $row.data('id')
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$row.fadeOut(200, () => $row.remove());
|
||||
} else {
|
||||
throw new Error((resp && resp.error) || 'Errore eliminazione');
|
||||
}
|
||||
}).catch(err => {
|
||||
$globalError.text(err.message || 'Errore durante l\'eliminazione.').show();
|
||||
$btn.prop('disabled', false);
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -257,56 +257,35 @@ class VisualLimsApiClient
|
||||
}
|
||||
|
||||
/**
|
||||
* Get raw/binary content from VisualLims API.
|
||||
* Used for PDF downloads from MediaFile/DownloadStream.
|
||||
* Recupera contenuto binario - Adattato per https://bvcpsitaly-elims.com/limsapi
|
||||
*/
|
||||
public function getRaw($endpoint)
|
||||
{
|
||||
$token = $this->getToken();
|
||||
|
||||
/*
|
||||
* Normal JSON OData calls use:
|
||||
* {$this->baseUrl}/api/odata/...
|
||||
*
|
||||
* Media file downloads use:
|
||||
* {$this->baseUrl}/api/MediaFile/DownloadStream...
|
||||
*/
|
||||
$url = rtrim($this->baseUrl, '/') . '/api/' . ltrim($endpoint, '/');
|
||||
// IMPORTANTE: usa /odata/ e NON /api/odata/
|
||||
$url = "{$this->baseUrl}/odata/{$endpoint}";
|
||||
|
||||
$ch = curl_init($url);
|
||||
|
||||
curl_setopt_array($ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/pdf,*/*"
|
||||
],
|
||||
CURLOPT_SSL_VERIFYPEER => false,
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
CURLOPT_FOLLOWLOCATION => true,
|
||||
CURLOPT_TIMEOUT => 60
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: */*"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$response = curl_exec($ch);
|
||||
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$contentType = curl_getinfo($ch, CURLINFO_CONTENT_TYPE);
|
||||
$curlError = curl_error($ch);
|
||||
|
||||
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
$curl_error = curl_error($ch);
|
||||
curl_close($ch);
|
||||
|
||||
if ($response === false) {
|
||||
throw new Exception("Errore cURL download raw: " . $curlError);
|
||||
throw new Exception("Errore cURL: " . $curl_error);
|
||||
}
|
||||
|
||||
if ($httpCode < 200 || $httpCode >= 300) {
|
||||
throw new Exception(
|
||||
"Errore HTTP {$httpCode} durante download raw. Content-Type: {$contentType}. Response: " .
|
||||
substr($response, 0, 500)
|
||||
);
|
||||
}
|
||||
|
||||
if (empty($response)) {
|
||||
throw new Exception("Risposta vuota dal download raw.");
|
||||
if ($http_code !== 200) {
|
||||
throw new Exception("HTTP {$http_code} su endpoint: " . $url);
|
||||
}
|
||||
|
||||
return $response;
|
||||
|
||||
@@ -1,448 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Helpers for JSON -> LIMS value bindings (table json_lims_binding).
|
||||
// Supports two kinds: 'custom' (template_mapping list fields, value -> import_data_details)
|
||||
// and 'fixed' (template_fixed_mapping list fields, id -> datadb column).
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixed-field metadata
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function binding_fixed_alias_map(): array
|
||||
{
|
||||
return [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
'ConsegnaRichiesta' => 'consegna_richiesta',
|
||||
];
|
||||
}
|
||||
|
||||
// Fixed-field keys that are LIMS list dropdowns (bindable). ConsegnaRichiesta is a date.
|
||||
function binding_fixed_is_list(string $key): bool
|
||||
{
|
||||
return in_array($key, [
|
||||
'ClienteResponsabile',
|
||||
'ClienteFornitore',
|
||||
'ClienteAnalisi',
|
||||
'MoltiplicatorePrezzo',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService',
|
||||
], true);
|
||||
}
|
||||
|
||||
function binding_fixed_column(string $key): ?string
|
||||
{
|
||||
return binding_fixed_alias_map()[$key] ?? null;
|
||||
}
|
||||
|
||||
// Auto-match only the small global lists; the client-based ones are huge / client-specific.
|
||||
function binding_fixed_auto_matchable(string $key): bool
|
||||
{
|
||||
return in_array($key, [
|
||||
'MoltiplicatorePrezzo',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService',
|
||||
], true);
|
||||
}
|
||||
|
||||
function binding_fixed_label(string $key): string
|
||||
{
|
||||
$labels = [
|
||||
'AnagraficaCertestObject' => 'Anagrafica Certest Object',
|
||||
'AnagraficaCertestService' => 'Anagrafica Certest Service',
|
||||
'MoltiplicatorePrezzo' => 'Moltiplicatore Prezzo',
|
||||
'ClienteResponsabile' => 'Cliente Responsabile',
|
||||
'ClienteFornitore' => 'Cliente Fornitore',
|
||||
'ClienteAnalisi' => 'Cliente Analisi',
|
||||
];
|
||||
return $labels[$key] ?? $key;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Bindable check (custom mapping row)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function binding_is_list_field(array $mapping): bool
|
||||
{
|
||||
$hasList = (int) ($mapping['has_list'] ?? 0) === 1;
|
||||
$isMultiChoice = strcasecmp(trim((string) ($mapping['data_type'] ?? '')), 'SceltaMultipla') === 0;
|
||||
return $hasList || $isMultiChoice;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Target keys + lookup / upsert
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function binding_target_custom(int $mappingId): string
|
||||
{
|
||||
return 'cf:' . $mappingId;
|
||||
}
|
||||
|
||||
function binding_target_fixed(int $templateId, string $fixedKey): string
|
||||
{
|
||||
return 'fx:' . $templateId . ':' . $fixedKey;
|
||||
}
|
||||
|
||||
function binding_lookup_target(PDO $pdo, string $targetKey, string $jsonValue): ?array
|
||||
{
|
||||
$stmt = $pdo->prepare(
|
||||
"SELECT id, lims_value_id, lims_value
|
||||
FROM json_lims_binding
|
||||
WHERE target_key = ? AND json_value = ?
|
||||
LIMIT 1"
|
||||
);
|
||||
$stmt->execute([$targetKey, $jsonValue]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
return $row ?: null;
|
||||
}
|
||||
|
||||
function binding_lookup(PDO $pdo, int $mappingId, string $jsonValue): ?array
|
||||
{
|
||||
return binding_lookup_target($pdo, binding_target_custom($mappingId), $jsonValue);
|
||||
}
|
||||
|
||||
function binding_lookup_fixed(PDO $pdo, int $templateId, string $fixedKey, string $jsonValue): ?array
|
||||
{
|
||||
return binding_lookup_target($pdo, binding_target_fixed($templateId, $fixedKey), $jsonValue);
|
||||
}
|
||||
|
||||
function binding_upsert_row(
|
||||
PDO $pdo,
|
||||
string $kind,
|
||||
int $templateId,
|
||||
?int $mappingId,
|
||||
?string $fixedFieldKey,
|
||||
string $targetKey,
|
||||
int $fieldId,
|
||||
string $jsonValue,
|
||||
int $limsValueId,
|
||||
string $limsValue,
|
||||
?int $createdBy
|
||||
): void {
|
||||
$stmt = $pdo->prepare(
|
||||
"INSERT INTO json_lims_binding
|
||||
(template_id, binding_kind, mapping_id, fixed_field_key, target_key, field_id, json_value, lims_value_id, lims_value, created_by)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
lims_value_id = VALUES(lims_value_id),
|
||||
lims_value = VALUES(lims_value),
|
||||
field_id = VALUES(field_id),
|
||||
template_id = VALUES(template_id),
|
||||
binding_kind = VALUES(binding_kind),
|
||||
mapping_id = VALUES(mapping_id),
|
||||
fixed_field_key = VALUES(fixed_field_key)"
|
||||
);
|
||||
$stmt->execute([
|
||||
$templateId, $kind, $mappingId, $fixedFieldKey, $targetKey,
|
||||
$fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy,
|
||||
]);
|
||||
}
|
||||
|
||||
function binding_upsert(
|
||||
PDO $pdo,
|
||||
int $templateId,
|
||||
int $mappingId,
|
||||
int $fieldId,
|
||||
string $jsonValue,
|
||||
int $limsValueId,
|
||||
string $limsValue,
|
||||
?int $createdBy
|
||||
): void {
|
||||
binding_upsert_row(
|
||||
$pdo, 'custom', $templateId, $mappingId, null,
|
||||
binding_target_custom($mappingId), $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy
|
||||
);
|
||||
}
|
||||
|
||||
function binding_upsert_fixed(
|
||||
PDO $pdo,
|
||||
int $templateId,
|
||||
string $fixedKey,
|
||||
string $jsonValue,
|
||||
int $limsValueId,
|
||||
string $limsValue,
|
||||
?int $createdBy
|
||||
): void {
|
||||
binding_upsert_row(
|
||||
$pdo, 'fixed', $templateId, null, $fixedKey,
|
||||
binding_target_fixed($templateId, $fixedKey), 0, $jsonValue, $limsValueId, $limsValue, $createdBy
|
||||
);
|
||||
}
|
||||
|
||||
function binding_delete_target(PDO $pdo, string $targetKey, string $jsonValue): int
|
||||
{
|
||||
$stmt = $pdo->prepare("DELETE FROM json_lims_binding WHERE target_key = ? AND json_value = ?");
|
||||
$stmt->execute([$targetKey, $jsonValue]);
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Custom-field LIMS values (cache/customfield_{id}.json) + auto-match
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function binding_get_lims_values(int $fieldId): array
|
||||
{
|
||||
static $memo = [];
|
||||
if ($fieldId <= 0) {
|
||||
return [];
|
||||
}
|
||||
if (array_key_exists($fieldId, $memo)) {
|
||||
return $memo[$fieldId];
|
||||
}
|
||||
|
||||
$cacheDir = dirname(__DIR__) . '/cache';
|
||||
$cacheFile = $cacheDir . '/customfield_' . $fieldId . '.json';
|
||||
|
||||
try {
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
$values = json_decode(file_get_contents($cacheFile), true);
|
||||
} else {
|
||||
require_once __DIR__ . '/VisualLimsApiClient.class.php';
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$data = $api->get("CustomField($fieldId)?\$expand=CustomFieldsValues");
|
||||
$values = $data['CustomFieldsValues'] ?? [];
|
||||
if (!is_dir($cacheDir)) {
|
||||
mkdir($cacheDir, 0777, true);
|
||||
}
|
||||
file_put_contents($cacheFile, json_encode($values));
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("binding_get_lims_values failed for field $fieldId: " . $e->getMessage());
|
||||
$values = [];
|
||||
}
|
||||
|
||||
if (!is_array($values)) {
|
||||
$values = [];
|
||||
}
|
||||
|
||||
return $memo[$fieldId] = $values;
|
||||
}
|
||||
|
||||
// Exactly one case-insensitive match by Valore -> that value, otherwise null.
|
||||
function binding_auto_match(array $limsValues, string $jsonValue): ?array
|
||||
{
|
||||
$needle = mb_strtolower(trim($jsonValue));
|
||||
if ($needle === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$matches = [];
|
||||
foreach ($limsValues as $v) {
|
||||
$valore = (string) ($v['Valore'] ?? '');
|
||||
if (mb_strtolower(trim($valore)) === $needle) {
|
||||
$matches[] = $v;
|
||||
}
|
||||
}
|
||||
|
||||
return count($matches) === 1 ? $matches[0] : null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Fixed-field LIMS values (per-field source) + auto-match
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function binding_template_idclient(PDO $pdo, int $templateId): int
|
||||
{
|
||||
$stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
|
||||
$stmt->execute([$templateId]);
|
||||
return (int) ($stmt->fetchColumn() ?: 0);
|
||||
}
|
||||
|
||||
function binding_client_label(array $client): string
|
||||
{
|
||||
$name = trim($client['Nominativo'] ?? '');
|
||||
$id = trim((string) ($client['IdCliente'] ?? ''));
|
||||
$code = trim((string) ($client['CodiceCliente'] ?? ''));
|
||||
|
||||
$parts = explode('_', $code);
|
||||
$suffix = trim($parts[1] ?? '');
|
||||
if ($suffix === '' && $code !== '') {
|
||||
$suffix = substr($code, 0, 1);
|
||||
}
|
||||
if ($suffix === '') {
|
||||
$suffix = '--';
|
||||
}
|
||||
|
||||
return $name . ' - ' . $suffix . ' (ID: ' . $id . ')';
|
||||
}
|
||||
|
||||
// Fetch an OData payload with a 1-hour file cache.
|
||||
function binding_cached_get($api, string $cacheFile, string $endpoint): array
|
||||
{
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
$data = json_decode(file_get_contents($cacheFile), true);
|
||||
} else {
|
||||
$data = $api->get($endpoint);
|
||||
$dir = dirname($cacheFile);
|
||||
if (!is_dir($dir)) {
|
||||
mkdir($dir, 0777, true);
|
||||
}
|
||||
file_put_contents($cacheFile, json_encode($data));
|
||||
}
|
||||
return is_array($data) ? $data : [];
|
||||
}
|
||||
|
||||
// Normalized LIMS value list for a fixed field: [['id' => int, 'text' => string], ...].
|
||||
function binding_get_fixed_values(PDO $pdo, string $fieldKey, int $templateId): array
|
||||
{
|
||||
static $memo = [];
|
||||
$memoKey = $fieldKey . '|' . $templateId;
|
||||
if (isset($memo[$memoKey])) {
|
||||
return $memo[$memoKey];
|
||||
}
|
||||
|
||||
$cacheDir = dirname(__DIR__) . '/cache';
|
||||
$out = [];
|
||||
|
||||
try {
|
||||
require_once __DIR__ . '/VisualLimsApiClient.class.php';
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
switch ($fieldKey) {
|
||||
case 'MoltiplicatorePrezzo':
|
||||
$data = binding_cached_get($api, "$cacheDir/moltiplicatori_prezzo.json", 'MoltiplicatorePrezzi');
|
||||
foreach (($data['value'] ?? []) as $r) {
|
||||
$out[] = ['id' => (int) ($r['IdMoltiplicatorePrezzo'] ?? 0), 'text' => (string) ($r['Descrizione'] ?? '')];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'AnagraficaCertestObject':
|
||||
case 'AnagraficaCertestService':
|
||||
$file = $fieldKey === 'AnagraficaCertestObject' ? 'anagrafica_certest_object.json' : 'anagrafica_certest_service.json';
|
||||
$data = binding_cached_get($api, "$cacheDir/$file", $fieldKey);
|
||||
foreach (($data['value'] ?? []) as $r) {
|
||||
$code = trim((string) ($r['Codice'] ?? ''));
|
||||
$text = ($code !== '' ? $code . ' - ' : '') . (string) ($r['NomeAnagrafica'] ?? '');
|
||||
$out[] = ['id' => (int) ($r['IdAnagrafica'] ?? 0), 'text' => $text];
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ClienteResponsabile':
|
||||
$idCliente = binding_template_idclient($pdo, $templateId);
|
||||
if ($idCliente > 0) {
|
||||
$data = binding_cached_get($api, "$cacheDir/cliente_responsabili_$idCliente.json", "Cliente($idCliente)?\$expand=Responsabili");
|
||||
foreach (($data['Responsabili'] ?? []) as $r) {
|
||||
$out[] = ['id' => (int) ($r['IdClienteResponsabile'] ?? 0), 'text' => (string) ($r['Nominativo'] ?? '')];
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'ClienteFornitore':
|
||||
case 'ClienteAnalisi':
|
||||
$endpoint = 'Cliente?' . http_build_query(['$select' => 'IdCliente,Nominativo,CodiceCliente', '$orderby' => 'Nominativo asc']);
|
||||
$data = binding_cached_get($api, "$cacheDir/clienti.json", $endpoint);
|
||||
foreach (($data['value'] ?? []) as $r) {
|
||||
$out[] = ['id' => (int) ($r['IdCliente'] ?? 0), 'text' => binding_client_label($r)];
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (Throwable $e) {
|
||||
error_log("binding_get_fixed_values($fieldKey) failed: " . $e->getMessage());
|
||||
$out = [];
|
||||
}
|
||||
|
||||
return $memo[$memoKey] = $out;
|
||||
}
|
||||
|
||||
// Exactly one case-insensitive match by text on a [{id,text}] list, otherwise null.
|
||||
function binding_auto_match_fixed(array $values, string $jsonValue): ?array
|
||||
{
|
||||
$needle = mb_strtolower(trim($jsonValue));
|
||||
if ($needle === '') {
|
||||
return null;
|
||||
}
|
||||
$matches = [];
|
||||
foreach ($values as $v) {
|
||||
if (mb_strtolower(trim((string) ($v['text'] ?? ''))) === $needle) {
|
||||
$matches[] = $v;
|
||||
}
|
||||
}
|
||||
return count($matches) === 1 ? $matches[0] : null;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// JSON node -> column matching (shared with import_insert custom logic)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
function binding_find_column_index(string $sourceColumn, array $columns): int
|
||||
{
|
||||
$sourceColumn = trim($sourceColumn);
|
||||
if ($sourceColumn === '') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
$columnsTrimmed = array_map('trim', $columns);
|
||||
$candidates = [
|
||||
$sourceColumn,
|
||||
preg_replace('/^data\[\]\./', '', $sourceColumn),
|
||||
preg_replace('/^data\.0\./', '', $sourceColumn),
|
||||
str_replace('data[].', 'data.0.', $sourceColumn),
|
||||
str_replace('data.0.', 'data[].', $sourceColumn),
|
||||
];
|
||||
$candidates = array_values(array_unique(array_filter(array_map('trim', $candidates), function ($v) {
|
||||
return $v !== '';
|
||||
})));
|
||||
|
||||
foreach ($candidates as $c) {
|
||||
$i = array_search($c, $columnsTrimmed, true);
|
||||
if ($i !== false) {
|
||||
return (int) $i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Apply resolved values
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
// Custom: write the resolved LIMS text into import_data_details for the given datadb ids.
|
||||
function binding_apply_to_details(
|
||||
PDO $pdo,
|
||||
int $mappingId,
|
||||
string $limsValue,
|
||||
array $datadbIds
|
||||
): int {
|
||||
$datadbIds = array_values(array_filter(array_map('intval', $datadbIds)));
|
||||
if (empty($datadbIds)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($datadbIds), '?'));
|
||||
$sql = "UPDATE import_data_details
|
||||
SET field_value = ?
|
||||
WHERE mapping_id = ?
|
||||
AND id IN ($placeholders)";
|
||||
|
||||
$params = array_merge([$limsValue, $mappingId], $datadbIds);
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute($params);
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
|
||||
// Fixed: write the resolved LIMS id into a whitelisted datadb column for the given ids.
|
||||
// $limsValueId null clears the column.
|
||||
function binding_apply_to_datadb(
|
||||
PDO $pdo,
|
||||
string $column,
|
||||
?int $limsValueId,
|
||||
array $datadbIds
|
||||
): int {
|
||||
if (!in_array($column, array_values(binding_fixed_alias_map()), true)) {
|
||||
throw new InvalidArgumentException("Invalid fixed-field column: $column");
|
||||
}
|
||||
$datadbIds = array_values(array_filter(array_map('intval', $datadbIds)));
|
||||
if (empty($datadbIds)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$placeholders = implode(',', array_fill(0, count($datadbIds), '?'));
|
||||
$sql = "UPDATE datadb SET `$column` = ? WHERE iddatadb IN ($placeholders)";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$stmt->execute(array_merge([$limsValueId], $datadbIds));
|
||||
return $stmt->rowCount();
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* debug_odata_metadata.php — TEMPORANEO, da rimuovere dopo il test.
|
||||
* Scopo: leggere $metadata (XML standard OData) per vedere:
|
||||
* - tutti gli EntitySet disponibili (non solo "Cliente")
|
||||
* - tutti i campi (Property) definiti per l'EntityType Cliente
|
||||
* Questo bypassa qualunque vista/proiezione ristretta lato server
|
||||
* applicata sull'endpoint "Cliente" normale.
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
|
||||
$dotenv->load();
|
||||
|
||||
$baseUrl = $_ENV['API_BASE_URL'];
|
||||
$username = $_ENV['API_USERNAME'];
|
||||
$password = $_ENV['API_PASSWORD'];
|
||||
|
||||
$scriptStart = microtime(true);
|
||||
|
||||
// ── 1. Autenticazione (stessa logica della classe originale) ──────────────
|
||||
$ch = curl_init("{$baseUrl}/api/authentication/authenticate");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'Username' => $username,
|
||||
'Password' => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$authResponse = curl_exec($ch);
|
||||
$authHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($authHttpCode !== 200) {
|
||||
echo "AUTH FAILED: HTTP {$authHttpCode}\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$tokenData = json_decode($authResponse, true);
|
||||
$token = null;
|
||||
if (is_array($tokenData) && isset($tokenData['token'])) {
|
||||
$token = $tokenData['token'];
|
||||
} elseif (is_string($tokenData)) {
|
||||
$token = trim($tokenData, '"');
|
||||
} else {
|
||||
$token = trim($authResponse, '"');
|
||||
}
|
||||
|
||||
if (empty($token)) {
|
||||
echo "TOKEN NOT RECEIVED\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$authElapsed = microtime(true) - $scriptStart;
|
||||
|
||||
// ── 2. Richiesta $metadata (XML, non JSON) ─────────────────────────────────
|
||||
// Cache-buster nell'URL: alcuni server/reverse-proxy potrebbero cachare per URL esatto.
|
||||
$cacheBuster = uniqid('cb_', true);
|
||||
$metadataUrl = "{$baseUrl}/api/odata/\$metadata?_cb={$cacheBuster}";
|
||||
|
||||
$metadataStart = microtime(true);
|
||||
|
||||
$ch = curl_init($metadataUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/xml"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HEADER, true); // includi header risposta per ispezione
|
||||
|
||||
$rawResponse = curl_exec($ch);
|
||||
$curlInfo = curl_getinfo($ch);
|
||||
$httpCode = $curlInfo['http_code'];
|
||||
$curlTotalTime = $curlInfo['total_time'];
|
||||
$curlError = curl_error($ch);
|
||||
$headerSize = $curlInfo['header_size'];
|
||||
curl_close($ch);
|
||||
|
||||
$metadataElapsed = microtime(true) - $metadataStart;
|
||||
|
||||
if ($rawResponse === false || $httpCode !== 200) {
|
||||
echo "METADATA REQUEST FAILED: HTTP {$httpCode}\nError: {$curlError}\n{$rawResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$responseHeaders = substr($rawResponse, 0, $headerSize);
|
||||
$xml = substr($rawResponse, $headerSize);
|
||||
|
||||
echo "==================== TIMING (prova che NON è cache) ====================\n";
|
||||
echo "Tempo autenticazione: " . round($authElapsed * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (curl total_time): " . round($curlTotalTime * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (wall clock PHP): " . round($metadataElapsed * 1000, 1) . " ms\n";
|
||||
echo "Cache-buster usato: {$cacheBuster}\n";
|
||||
echo "Dimensione XML ricevuto: " . strlen($xml) . " bytes\n\n";
|
||||
|
||||
echo "==================== HEADER RISPOSTA (controlla se c'è Cache-Control/Age/X-Cache) ====================\n";
|
||||
echo $responseHeaders . "\n";
|
||||
|
||||
// ── 3. Estrai tutti gli EntitySet disponibili ──────────────────────────────
|
||||
echo "==================== ENTITY SETS DISPONIBILI ====================\n";
|
||||
if (preg_match_all('/<EntitySet Name="([^"]+)"/', $xml, $matches)) {
|
||||
foreach ($matches[1] as $name) {
|
||||
echo "- {$name}\n";
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntitySet trovato — controlla il formato XML grezzo sotto)\n";
|
||||
}
|
||||
|
||||
// ── 4. Estrai tutte le Property dell'EntityType "Cliente" (e simili) ──────
|
||||
echo "\n==================== CAMPI PER ENTITYTYPE CHE CONTENGONO 'Client' ====================\n";
|
||||
if (preg_match_all('/<EntityType Name="([^"]*[Cc]lient[^"]*)"[^>]*>(.*?)<\/EntityType>/s', $xml, $entityMatches, PREG_SET_ORDER)) {
|
||||
foreach ($entityMatches as $entity) {
|
||||
$entityName = $entity[1];
|
||||
$entityBody = $entity[2];
|
||||
echo "\n--- EntityType: {$entityName} ---\n";
|
||||
if (preg_match_all('/<Property Name="([^"]+)" Type="([^"]+)"/', $entityBody, $propMatches, PREG_SET_ORDER)) {
|
||||
foreach ($propMatches as $prop) {
|
||||
echo " {$prop[1]} ({$prop[2]})\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntityType con 'Client' nel nome trovato)\n";
|
||||
}
|
||||
|
||||
// ── 5. Salva l'XML grezzo completo per ispezione manuale, se serve ────────
|
||||
file_put_contents(__DIR__ . '/metadata_debug.xml', $xml);
|
||||
echo "\n\nXML completo salvato in: metadata_debug.xml (rimuovilo dopo il test)\n";
|
||||
@@ -10,6 +10,10 @@ $data = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$sourceIddatadb = isset($data['source_iddatadb']) ? (int)$data['source_iddatadb'] : 0;
|
||||
$targetList = $data['target_iddatadb_list'] ?? [];
|
||||
// Default true: se il client non manda il parametro, cloniamo comunque le note
|
||||
// (comportamento di default richiesto, coerente con la checkbox pre-spuntata)
|
||||
$cloneNotes = array_key_exists('clone_notes', $data) ? !empty($data['clone_notes']) : true;
|
||||
$cloneAnalyses = !empty($data['clone_analyses']);
|
||||
|
||||
$targetIds = array_values(array_unique(array_filter(array_map('intval', (array)$targetList), function ($v) use ($sourceIddatadb) {
|
||||
return $v > 0 && $v !== $sourceIddatadb;
|
||||
@@ -62,6 +66,23 @@ try {
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmtLoadAnalyses = $pdo->prepare("
|
||||
SELECT idmatrice, analysis_recordkey, analysis_name, analysis_method,
|
||||
analysis_level, is_web_selectable, is_accredited
|
||||
FROM identification_parts_analyses
|
||||
WHERE part_id = ?
|
||||
ORDER BY id ASC
|
||||
");
|
||||
|
||||
$stmtInsertAnalysis = $pdo->prepare("
|
||||
INSERT INTO identification_parts_analyses
|
||||
(part_id, iddatadb, idmatrice, analysis_recordkey, analysis_name, analysis_method,
|
||||
analysis_level, is_web_selectable, is_accredited, created_at, updated_at)
|
||||
VALUES
|
||||
(:part_id, :iddatadb, :idmatrice, :analysis_recordkey, :analysis_name, :analysis_method,
|
||||
:analysis_level, :is_web_selectable, :is_accredited, NOW(), NOW())
|
||||
");
|
||||
|
||||
$stmtInsertCF = $pdo->prepare("
|
||||
INSERT INTO identification_parts_customfields
|
||||
(part_id, field_id, value_id, value_text, created_at, updated_at)
|
||||
@@ -71,6 +92,7 @@ try {
|
||||
|
||||
$details = [];
|
||||
$totalClonedParts = 0;
|
||||
$totalClonedAnalyses = 0;
|
||||
|
||||
// 3. Clone source parts to each target record
|
||||
foreach ($targetIds as $targetIddatadb) {
|
||||
@@ -83,7 +105,7 @@ try {
|
||||
':part_description' => $part['part_description'],
|
||||
':mix' => $part['mix'] ?? 'N',
|
||||
':idmatrice' => $part['idmatrice'] !== '' ? $part['idmatrice'] : null,
|
||||
':note' => $part['note'] ?? null,
|
||||
':note' => $cloneNotes ? ($part['note'] ?? null) : null,
|
||||
':dateexpiry' => $part['dateexpiry'] ?? null,
|
||||
]);
|
||||
|
||||
@@ -102,6 +124,27 @@ try {
|
||||
]);
|
||||
}
|
||||
|
||||
// Clone linked analyses, if requested
|
||||
if ($cloneAnalyses) {
|
||||
$stmtLoadAnalyses->execute([(int)$part['id']]);
|
||||
$analyses = $stmtLoadAnalyses->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
foreach ($analyses as $an) {
|
||||
$stmtInsertAnalysis->execute([
|
||||
':part_id' => $newPartId,
|
||||
':iddatadb' => $targetIddatadb,
|
||||
':idmatrice' => ($an['idmatrice'] !== null && $an['idmatrice'] !== '') ? (int)$an['idmatrice'] : null,
|
||||
':analysis_recordkey' => $an['analysis_recordkey'],
|
||||
':analysis_name' => $an['analysis_name'] ?? null,
|
||||
':analysis_method' => $an['analysis_method'] ?? null,
|
||||
':analysis_level' => ($an['analysis_level'] !== null && $an['analysis_level'] !== '') ? (int)$an['analysis_level'] : null,
|
||||
':is_web_selectable' => (int)($an['is_web_selectable'] ?? 0),
|
||||
':is_accredited' => (int)($an['is_accredited'] ?? 0),
|
||||
]);
|
||||
$totalClonedAnalyses++;
|
||||
}
|
||||
}
|
||||
|
||||
$clonedCountForTarget++;
|
||||
$totalClonedParts++;
|
||||
}
|
||||
@@ -119,6 +162,7 @@ try {
|
||||
'source_iddatadb' => $sourceIddatadb,
|
||||
'cloned_targets' => count($targetIds),
|
||||
'total_cloned_parts' => $totalClonedParts,
|
||||
'total_cloned_analyses' => $totalClonedAnalyses,
|
||||
'details' => $details
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
|
||||
@@ -23,19 +23,17 @@ if ((int)$stmt->fetchColumn() > 0) {
|
||||
}
|
||||
|
||||
/**
|
||||
* FIXED FIELDS STANDARD (no UI selection)
|
||||
* is_manual always 1
|
||||
* is_visible_import default 1
|
||||
* FIXED FIELDS STANDARD
|
||||
*/
|
||||
$fixedFields = [
|
||||
// fixed_field_key, data_type
|
||||
['ClienteResponsabile', 'INT'],
|
||||
['ClienteFornitore', 'INT'],
|
||||
['ClienteAnalisi', 'INT'],
|
||||
['MoltiplicatorePrezzo', 'INT'],
|
||||
['AnagraficaCertestObject', 'INT'],
|
||||
['ClienteResponsabile', 'INT'],
|
||||
['ClienteFornitore', 'INT'],
|
||||
['ClienteAnalisi', 'INT'],
|
||||
['ClienteFatturazione', 'INT'], // ← NUOVO
|
||||
['MoltiplicatorePrezzo', 'INT'],
|
||||
['AnagraficaCertestObject', 'INT'],
|
||||
['AnagraficaCertestService', 'INT'],
|
||||
['ConsegnaRichiesta', 'DATE'],
|
||||
['ConsegnaRichiesta', 'DATE'],
|
||||
];
|
||||
|
||||
|
||||
@@ -43,12 +41,11 @@ 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)
|
||||
");
|
||||
|
||||
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([
|
||||
@@ -58,16 +55,15 @@ try {
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$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
|
||||
");
|
||||
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);
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* debug_odata_metadata.php — TEMPORANEO, da rimuovere dopo il test.
|
||||
* Scopo: leggere $metadata (XML standard OData) per vedere:
|
||||
* - tutti gli EntitySet disponibili (non solo "Cliente")
|
||||
* - tutti i campi (Property) definiti per l'EntityType Cliente
|
||||
* Questo bypassa qualunque vista/proiezione ristretta lato server
|
||||
* applicata sull'endpoint "Cliente" normale.
|
||||
*/
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
use Dotenv\Dotenv;
|
||||
|
||||
header('Content-Type: text/plain; charset=utf-8');
|
||||
ini_set('display_errors', '1');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 2));
|
||||
$dotenv->load();
|
||||
|
||||
$baseUrl = $_ENV['API_BASE_URL'];
|
||||
$username = $_ENV['API_USERNAME'];
|
||||
$password = $_ENV['API_PASSWORD'];
|
||||
|
||||
$scriptStart = microtime(true);
|
||||
|
||||
// ── 1. Autenticazione (stessa logica della classe originale) ──────────────
|
||||
$ch = curl_init("{$baseUrl}/api/authentication/authenticate");
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_POST, true);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
|
||||
'Username' => $username,
|
||||
'Password' => $password
|
||||
]));
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Accept: application/json'
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
$authResponse = curl_exec($ch);
|
||||
$authHttpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
curl_close($ch);
|
||||
|
||||
if ($authHttpCode !== 200) {
|
||||
echo "AUTH FAILED: HTTP {$authHttpCode}\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$tokenData = json_decode($authResponse, true);
|
||||
$token = null;
|
||||
if (is_array($tokenData) && isset($tokenData['token'])) {
|
||||
$token = $tokenData['token'];
|
||||
} elseif (is_string($tokenData)) {
|
||||
$token = trim($tokenData, '"');
|
||||
} else {
|
||||
$token = trim($authResponse, '"');
|
||||
}
|
||||
|
||||
if (empty($token)) {
|
||||
echo "TOKEN NOT RECEIVED\n{$authResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$authElapsed = microtime(true) - $scriptStart;
|
||||
|
||||
// ── 2. Richiesta $metadata (XML, non JSON) ─────────────────────────────────
|
||||
// Cache-buster nell'URL: alcuni server/reverse-proxy potrebbero cachare per URL esatto.
|
||||
$cacheBuster = uniqid('cb_', true);
|
||||
$metadataUrl = "{$baseUrl}/api/odata/\$metadata?_cb={$cacheBuster}";
|
||||
|
||||
$metadataStart = microtime(true);
|
||||
|
||||
$ch = curl_init($metadataUrl);
|
||||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
"Authorization: Bearer {$token}",
|
||||
"Accept: application/xml"
|
||||
]);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
|
||||
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
|
||||
|
||||
curl_setopt($ch, CURLOPT_HEADER, true); // includi header risposta per ispezione
|
||||
|
||||
$rawResponse = curl_exec($ch);
|
||||
$curlInfo = curl_getinfo($ch);
|
||||
$httpCode = $curlInfo['http_code'];
|
||||
$curlTotalTime = $curlInfo['total_time'];
|
||||
$curlError = curl_error($ch);
|
||||
$headerSize = $curlInfo['header_size'];
|
||||
curl_close($ch);
|
||||
|
||||
$metadataElapsed = microtime(true) - $metadataStart;
|
||||
|
||||
if ($rawResponse === false || $httpCode !== 200) {
|
||||
echo "METADATA REQUEST FAILED: HTTP {$httpCode}\nError: {$curlError}\n{$rawResponse}\n";
|
||||
exit;
|
||||
}
|
||||
|
||||
$responseHeaders = substr($rawResponse, 0, $headerSize);
|
||||
$xml = substr($rawResponse, $headerSize);
|
||||
|
||||
echo "==================== TIMING (prova che NON è cache) ====================\n";
|
||||
echo "Tempo autenticazione: " . round($authElapsed * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (curl total_time): " . round($curlTotalTime * 1000, 1) . " ms\n";
|
||||
echo "Tempo chiamata \$metadata (wall clock PHP): " . round($metadataElapsed * 1000, 1) . " ms\n";
|
||||
echo "Cache-buster usato: {$cacheBuster}\n";
|
||||
echo "Dimensione XML ricevuto: " . strlen($xml) . " bytes\n\n";
|
||||
|
||||
echo "==================== HEADER RISPOSTA (controlla se c'è Cache-Control/Age/X-Cache) ====================\n";
|
||||
echo $responseHeaders . "\n";
|
||||
|
||||
// ── 3. Estrai tutti gli EntitySet disponibili ──────────────────────────────
|
||||
echo "==================== ENTITY SETS DISPONIBILI ====================\n";
|
||||
if (preg_match_all('/<EntitySet Name="([^"]+)"/', $xml, $matches)) {
|
||||
foreach ($matches[1] as $name) {
|
||||
echo "- {$name}\n";
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntitySet trovato — controlla il formato XML grezzo sotto)\n";
|
||||
}
|
||||
|
||||
// ── 4. Estrai tutte le Property dell'EntityType "Cliente" (e simili) ──────
|
||||
echo "\n==================== CAMPI PER ENTITYTYPE CHE CONTENGONO 'Client' ====================\n";
|
||||
if (preg_match_all('/<EntityType Name="([^"]*[Cc]lient[^"]*)"[^>]*>(.*?)<\/EntityType>/s', $xml, $entityMatches, PREG_SET_ORDER)) {
|
||||
foreach ($entityMatches as $entity) {
|
||||
$entityName = $entity[1];
|
||||
$entityBody = $entity[2];
|
||||
echo "\n--- EntityType: {$entityName} ---\n";
|
||||
if (preg_match_all('/<Property Name="([^"]+)" Type="([^"]+)"/', $entityBody, $propMatches, PREG_SET_ORDER)) {
|
||||
foreach ($propMatches as $prop) {
|
||||
echo " {$prop[1]} ({$prop[2]})\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
echo "(nessun EntityType con 'Client' nel nome trovato)\n";
|
||||
}
|
||||
|
||||
// ── 5. Salva l'XML grezzo completo per ispezione manuale, se serve ────────
|
||||
file_put_contents(__DIR__ . '/metadata_debug.xml', $xml);
|
||||
echo "\n\nXML completo salvato in: metadata_debug.xml (rimuovilo dopo il test)\n";
|
||||
@@ -1,40 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Elimina un binding JSON -> LIMS. Ritorna JSON.
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($_POST['id'] ?? 0);
|
||||
if ($id <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->prepare("DELETE FROM json_lims_binding WHERE id = ?");
|
||||
$stmt->execute([$id]);
|
||||
echo json_encode(['success' => true, 'deleted' => $stmt->rowCount()]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,46 +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);
|
||||
|
||||
try {
|
||||
$idRapportoFile = isset($_GET['id_rapporto_file']) ? (int)$_GET['id_rapporto_file'] : 0;
|
||||
|
||||
if ($idRapportoFile <= 0) {
|
||||
throw new Exception("Parametro id_rapporto_file mancante o non valido.");
|
||||
}
|
||||
|
||||
/*
|
||||
* This endpoint returns the PDF binary stream.
|
||||
* Do not call this with the normal get() method because get() expects JSON.
|
||||
*/
|
||||
$endpoint = "MediaFile/DownloadStream?objectType=RapportoFile&propertyName=FileContent&objectKey={$idRapportoFile}";
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$pdfContent = $api->getRaw($endpoint);
|
||||
|
||||
if (empty($pdfContent)) {
|
||||
throw new Exception("PDF vuoto o non ricevuto dal server.");
|
||||
}
|
||||
|
||||
$fileName = "rapporto_{$idRapportoFile}.pdf";
|
||||
|
||||
header('Content-Type: application/pdf');
|
||||
header('Content-Disposition: inline; filename="' . $fileName . '"');
|
||||
header('Content-Length: ' . strlen($pdfContent));
|
||||
header('Cache-Control: private, max-age=0, must-revalidate');
|
||||
header('Pragma: public');
|
||||
|
||||
echo $pdfContent;
|
||||
exit;
|
||||
} 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);
|
||||
}
|
||||
@@ -331,3 +331,46 @@
|
||||
2026-04-30 15:01:25 - Errore nel recupero dati: HTTP 404, Risposta: {"title":"Not Found","status":404,"detail":"Not Found","instance":"GET /api/odata/Rapporto(2621521)","errorCode":"d25cbd678"}
|
||||
2026-04-30 15:02:04 - Errore nel recupero dati: HTTP 404, Risposta:
|
||||
2026-04-30 15:03:19 - Errore nella richiesta: URL rejected: Malformed input to a URL function
|
||||
2026-06-16 14:29:15 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21037 ms: Couldn't connect to server, Risposta:
|
||||
|
||||
|
||||
2026-06-16 14:29:36 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21099 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:03 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21076 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:03 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21083 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:03 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21083 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:34 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21043 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:55 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21053 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:30:55 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21050 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:31:46 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21048 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:31:46 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21043 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:31:46 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21042 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:32:07 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21047 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:09 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21058 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:09 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21057 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:09 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21057 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:33:30 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21047 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:11 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21062 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:11 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21064 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:11 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21065 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:33 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21049 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:33 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21057 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:36:33 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21053 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:13 [MoltiplicatorePrezzo] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21032 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:14 [AnagraficaCertestService] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21031 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:14 [AnagraficaCertestObject] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21027 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 14:37:35 [ClienteResponsabile] Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21054 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:13:55 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21067 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:14:25 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21065 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:14:46 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21071 ms: Couldn't connect to server, Risposta:
|
||||
2026-06-16 17:14:46 - Autenticazione fallita: HTTP 0, Errore cURL: Failed to connect to bvcpsitaly-elims.com port 443 after 21078 ms: Couldn't connect to server, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:33 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-09 08:06:41 - Autenticazione fallita: HTTP 0, Errore cURL: Could not resolve host: bvcpsitaly-elims.com, Risposta:
|
||||
2026-07-10 08:12:24 - Errore nella richiesta: SSL connection timeout
|
||||
|
||||
@@ -15,6 +15,40 @@ if (!is_dir($logDir)) {
|
||||
|
||||
$uploadDir = realpath(__DIR__ . '/../photostrf') . '/';
|
||||
|
||||
/**
|
||||
* Resolve a datadb_photos.file_path to an absolute file path on disk.
|
||||
*/
|
||||
function resolvePhotoFullPath(string $filePath, string $uploadDir, string $userareaDir): ?string
|
||||
{
|
||||
$filePath = trim($filePath);
|
||||
if ($filePath === '') {
|
||||
return null;
|
||||
}
|
||||
|
||||
$base = rtrim($uploadDir, '/');
|
||||
$candidates = [];
|
||||
|
||||
// Userarea-relative form: "../photostrf/annotated/x.png" → resolve from __DIR__.
|
||||
if (strncmp($filePath, '../', 3) === 0) {
|
||||
$candidates[] = rtrim($userareaDir, '/') . '/' . $filePath;
|
||||
}
|
||||
|
||||
// Bare filename or "annotated/x.png" subpath - resolve from photostrf/.
|
||||
$candidates[] = $base . '/' . ltrim($filePath, './');
|
||||
|
||||
// Safety net for any odd/legacy value.
|
||||
$candidates[] = $base . '/' . basename($filePath);
|
||||
|
||||
foreach ($candidates as $candidate) {
|
||||
$real = realpath($candidate);
|
||||
if ($real && file_exists($real)) {
|
||||
return $real;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// 🔹 Base URL API
|
||||
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
|
||||
|
||||
@@ -116,13 +150,14 @@ try {
|
||||
// 🔹 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,
|
||||
SELECT d.idclient AS clienteId, et.idschema AS schemaId, et.id AS templateId,
|
||||
d.cliente_responsabile_id,
|
||||
d.moltiplicatore_prezzo_id,
|
||||
d.anagrafica_certest_object_id,
|
||||
d.anagrafica_certest_service_id,
|
||||
d.cliente_fornitore_id,
|
||||
d.clienteAnalisi,
|
||||
d.ClienteFatturazione,
|
||||
d.consegna_richiesta
|
||||
FROM datadb as d
|
||||
INNER JOIN excel_templates as et ON d.templateid = et.id
|
||||
@@ -138,6 +173,7 @@ try {
|
||||
|
||||
$clienteId = (int) $result['clienteId'];
|
||||
$schemaId = (int) $result['schemaId'];
|
||||
$templateId = (int) $result['templateId'];
|
||||
|
||||
// Extract fixed fields (nullable INTs)
|
||||
$clienteResponsabile = !empty($result['cliente_responsabile_id']) ? (int) $result['cliente_responsabile_id'] : null;
|
||||
@@ -146,11 +182,12 @@ try {
|
||||
$anagraficaService = !empty($result['anagrafica_certest_service_id']) ? (int) $result['anagrafica_certest_service_id'] : null;
|
||||
$clienteFornitore = !empty($result['cliente_fornitore_id']) ? (int) $result['cliente_fornitore_id'] : null;
|
||||
$clienteAnalisi = !empty($result['clienteAnalisi']) ? (int) $result['clienteAnalisi'] : null;
|
||||
$clienteFatturazione = !empty($result['ClienteFatturazione']) ? (int) $result['ClienteFatturazione'] : null;
|
||||
$consegnaRichiesta = !empty($result['consegna_richiesta']) ? $result['consegna_richiesta'] : null;
|
||||
|
||||
// 🔹 STEP 3: Fetch Parts (including idmatrice and part id for custom fields)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id AS part_id, part_number, part_description, material, color, mix, idmatrice, dateexpiry
|
||||
SELECT id AS part_id, part_number, part_description, material, color, mix, idmatrice, dateexpiry, note
|
||||
FROM identification_parts
|
||||
WHERE iddatadb = :iddatadb
|
||||
AND part_description IS NOT NULL
|
||||
@@ -176,6 +213,7 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 🔹 STEP 4a: Auto-populate export_date / export_time fields
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE import_data_details idd
|
||||
@@ -199,7 +237,9 @@ try {
|
||||
idd.field_value,
|
||||
m.field_label,
|
||||
m.schema_id,
|
||||
m.field_id
|
||||
m.field_id,
|
||||
m.field_order,
|
||||
m.is_addnote
|
||||
FROM
|
||||
import_data_details as idd
|
||||
JOIN template_mapping m ON idd.mapping_id = m.id
|
||||
@@ -219,6 +259,27 @@ try {
|
||||
$valueMap[(int) $row['field_id']] = $row['field_value'];
|
||||
}
|
||||
|
||||
// 🔹 Note aggiuntive: custom field flaggati is_addnote e valorizzati.
|
||||
// Valori a livello di commessa → identici su tutte le parti.
|
||||
$addnoteChunks = [];
|
||||
$addnoteRows = array_filter($rows, fn($r) => (int)($r['is_addnote'] ?? 0) === 1);
|
||||
|
||||
usort($addnoteRows, function ($a, $b) {
|
||||
$oa = (int)($a['field_order'] ?? 9999);
|
||||
$ob = (int)($b['field_order'] ?? 9999);
|
||||
if ($oa !== $ob) return $oa <=> $ob;
|
||||
return strcmp((string)($a['field_label'] ?? ''), (string)($b['field_label'] ?? ''));
|
||||
});
|
||||
|
||||
foreach ($addnoteRows as $r) {
|
||||
$val = trim((string)($r['field_value'] ?? ''));
|
||||
if ($val !== '') {
|
||||
$addnoteChunks[] = $val;
|
||||
}
|
||||
}
|
||||
|
||||
$addnoteText = implode(' - ', $addnoteChunks);
|
||||
|
||||
// Logga i fieldValues in error_log
|
||||
$logFieldValues = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT);
|
||||
error_log($logFieldValues);
|
||||
@@ -240,6 +301,7 @@ try {
|
||||
"AnagraficaCertestService" => $anagraficaService,
|
||||
"ClienteFornitore" => $clienteFornitore, // PLACEHOLDER — to be implemented
|
||||
"ClienteAnalisi" => $clienteAnalisi, // PLACEHOLDER — to be implemented
|
||||
// "ClienteFatturazione" => $clienteFatturazione, // ⚠️ TEMP DISABLED - problemi API lato LIMS
|
||||
// DeliveryRequest goes to Campione, not CommessaWeb
|
||||
];
|
||||
|
||||
@@ -272,13 +334,33 @@ try {
|
||||
throw new Exception("Invalid or missing idmatrice for part: " . ($part["part_number"] ?? "Unknown"));
|
||||
}
|
||||
|
||||
$partDescription = $part["part_description"] ?? "";
|
||||
$partNote = trim((string)($part["note"] ?? ""));
|
||||
|
||||
// Note aggiuntive dai custom field flaggati is_addnote
|
||||
$partDescription = $part["part_description"] ?? "";
|
||||
$partNote = trim((string)($part["note"] ?? ""));
|
||||
|
||||
// Componi NoteWeb: descrizione [- note] [- addnote di commessa]
|
||||
$noteParts = [];
|
||||
if ($partDescription !== "") {
|
||||
$noteParts[] = $partDescription;
|
||||
}
|
||||
if ($partNote !== "") {
|
||||
$noteParts[] = $partNote;
|
||||
}
|
||||
if ($addnoteText !== "") {
|
||||
$noteParts[] = $addnoteText;
|
||||
}
|
||||
$noteWebValue = implode(" - ", $noteParts);
|
||||
|
||||
$campionePayload = [
|
||||
"Commessa" => $commessaId,
|
||||
"Matrice" => $matriceId,
|
||||
"SottoMatrice" => null,
|
||||
"SchemaCustomField" => $schemaId,
|
||||
// "Riferimento" => $part["part_description"] ?? "",
|
||||
"NoteWeb" => $part["part_description"] ?? "",
|
||||
"NoteWeb" => $noteWebValue,
|
||||
"ConsegnaRichiesta" => !empty($part["dateexpiry"]) ? $part["dateexpiry"] : $consegnaRichiesta,
|
||||
];
|
||||
|
||||
@@ -420,11 +502,10 @@ try {
|
||||
$logContentPhotos .= "=== Campione {$campioneId} (main) ===\n";
|
||||
|
||||
foreach ($photos as $photo) {
|
||||
$photoPath = $uploadDir . '/' . ltrim($photo['file_path'], './');
|
||||
$fullPath = realpath($photoPath);
|
||||
$fullPath = resolvePhotoFullPath($photo['file_path'], $uploadDir, __DIR__);
|
||||
|
||||
if (!$fullPath || !file_exists($fullPath)) {
|
||||
$logContentPhotos .= "SKIP (file not found): {$photoPath}\n";
|
||||
if ($fullPath === null) {
|
||||
$logContentPhotos .= "SKIP (file not found): file_path='{$photo['file_path']}'\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -594,10 +675,10 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
// 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, and status
|
||||
// 🔹 STEP 8: Update datadb with idcommessaweb, commessaweb, status and export date
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE datadb
|
||||
SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l'
|
||||
SET idcommessaweb = :idcommessaweb, commessaweb = :commessaweb, status = 'l', export_date = NOW()
|
||||
WHERE iddatadb = :iddatadb
|
||||
");
|
||||
$stmt->execute([
|
||||
|
||||
@@ -16,9 +16,11 @@ try {
|
||||
exit;
|
||||
}
|
||||
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv as a side-effect
|
||||
|
||||
$webOnly = isset($_GET['web_only']) ? (int)$_GET['web_only'] : 1;
|
||||
$refresh = isset($_GET['refresh']) ? (int)$_GET['refresh'] : 0;
|
||||
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
|
||||
|
||||
$filterString = "Matrice/IdMatrice eq $idMatrice";
|
||||
|
||||
@@ -32,19 +34,34 @@ try {
|
||||
$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);
|
||||
// Cache file matches the one warm_cache.php writes (web_only=1 is what the analysis modal asks for).
|
||||
// Caching skipped in simulate mode to avoid polluting real-data cache files.
|
||||
$cache_file = __DIR__ . '/cache/analisi_matrice_' . $idMatrice . ($webOnly === 1 ? '' : '_all') . '.json';
|
||||
$data = null;
|
||||
$fromCache = false;
|
||||
|
||||
$data = $api->get($endpoint, []);
|
||||
if (!$simulate && !$refresh && file_exists($cache_file) && (time() - filemtime($cache_file) < 3600)) { // 1 ora
|
||||
$data = json_decode(file_get_contents($cache_file), true);
|
||||
$fromCache = is_array($data);
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_by_matrice_response.json',
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
if (!$fromCache) {
|
||||
$data = $api->get($endpoint, []);
|
||||
if (!$simulate) {
|
||||
file_put_contents($cache_file, json_encode(['value' => $data['value'] ?? []], JSON_UNESCAPED_UNICODE));
|
||||
}
|
||||
}
|
||||
|
||||
if ($debug === 1) {
|
||||
file_put_contents(__DIR__ . '/last_url_analisi.txt', $full_url . PHP_EOL, FILE_APPEND);
|
||||
file_put_contents(
|
||||
__DIR__ . '/analisi_by_matrice_response.json',
|
||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
echo json_encode([
|
||||
'endpoint' => $endpoint,
|
||||
'full_url' => $full_url,
|
||||
'from_cache' => $fromCache,
|
||||
'raw_response' => $data
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
|
||||
@@ -30,7 +30,7 @@ try {
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente',
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
echo json_encode(['value' => $fakeClients]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// ── DEBUG MODE ───────────────────────────────────────────────────────
|
||||
// Chiamata: get_clienti.php?debug=1
|
||||
// Bypassa cache e $select per vedere TUTTI i campi grezzi restituiti
|
||||
// dall'API per l'entità Cliente. Usare solo per ispezione manuale,
|
||||
// NON lasciare linkato/usato in produzione dal frontend.
|
||||
if (($_GET['debug'] ?? '') === '1') {
|
||||
$debugParams = [
|
||||
'$top' => 5,
|
||||
'$orderby' => 'Nominativo asc'
|
||||
// Nessun $select: chiediamo tutti i campi disponibili
|
||||
];
|
||||
$debugEndpoint = "Cliente?" . http_build_query($debugParams);
|
||||
$debugData = $api->get($debugEndpoint);
|
||||
$debugClients = $debugData['value'] ?? $debugData;
|
||||
|
||||
echo json_encode([
|
||||
'mode' => 'debug - no $select, cache bypassed',
|
||||
'top_level_keys' => is_array($debugData) ? array_keys($debugData) : null,
|
||||
'fields_found_in_first_record' => isset($debugClients[0]) ? array_keys($debugClients[0]) : null,
|
||||
'sample_records' => $debugClients,
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
exit;
|
||||
}
|
||||
// ── FINE DEBUG MODE ──────────────────────────────────────────────────
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
// Costruisce query string con encoding corretto
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Componi endpoint finale
|
||||
$endpoint = "Cliente?$queryString";
|
||||
|
||||
// Funzione per eseguire la chiamata con retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
// Tenta la chiamata API
|
||||
$data = $api->get($endpoint);
|
||||
// Salva risposta per debug
|
||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
|
||||
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
|
||||
// Forza il refresh del token
|
||||
try {
|
||||
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
|
||||
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
// Ritarda leggermente prima del retry
|
||||
usleep(500000); // 500ms
|
||||
continue;
|
||||
}
|
||||
// Altri errori non gestiti dal retry
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Esegui la chiamata con retry
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
$json = json_encode($data);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/VisualLimsApiClient.class.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Disable PHP error display
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
try {
|
||||
$api = VisualLimsApiClient::getInstance(); // also loads dotenv
|
||||
|
||||
// In simulate mode: return fake clients built from idclient values already in datadb.
|
||||
// This ensures client dropdowns auto-select the correct row idclient, which in turn
|
||||
// triggers the ClienteResponsabile select to populate via the mock.
|
||||
if (($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true') {
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$stmt = $pdo->query("SELECT DISTINCT idclient FROM datadb WHERE idclient IS NOT NULL AND idclient > 0 ORDER BY idclient ASC");
|
||||
$ids = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
$fakeClients = array_map(fn($id) => [
|
||||
'IdCliente' => (int) $id,
|
||||
'Nominativo' => "Cliente Simulato {$id}",
|
||||
'CodiceCliente' => "SIM_{$id}",
|
||||
], $ids);
|
||||
echo json_encode(['value' => $fakeClients]);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Parametri OData
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
// Costruisce query string con encoding corretto
|
||||
$queryString = http_build_query($params);
|
||||
|
||||
// Componi endpoint finale
|
||||
$endpoint = "Cliente?$queryString";
|
||||
|
||||
// Funzione per eseguire la chiamata con retry
|
||||
function makeApiRequest($api, $endpoint, $maxRetries = 3)
|
||||
{
|
||||
for ($retry = 0; $retry < $maxRetries; $retry++) {
|
||||
try {
|
||||
// Tenta la chiamata API
|
||||
$data = $api->get($endpoint);
|
||||
// Salva risposta per debug
|
||||
file_put_contents(__DIR__ . '/clienti_response.json', json_encode($data, JSON_PRETTY_PRINT));
|
||||
return $data;
|
||||
} catch (Exception $e) {
|
||||
$errorMessage = $e->getMessage();
|
||||
// Controlla se l'errore è legato all'autenticazione (HTTP 400 con messaggio specifico)
|
||||
if (strpos($errorMessage, 'HTTP 400') !== false && strpos($errorMessage, 'Cannot persist the object') !== false) {
|
||||
// Forza il refresh del token
|
||||
try {
|
||||
// Assumi che VisualLimsApiClient abbia un metodo per il refresh del token
|
||||
$api->refreshToken(); // Da implementare in VisualLimsApiClient se non esiste
|
||||
error_log("Tentativo $retry: Refresh token eseguito per endpoint $endpoint");
|
||||
} catch (Exception $refreshEx) {
|
||||
error_log("Errore durante il refresh del token: " . $refreshEx->getMessage());
|
||||
throw new Exception("Impossibile eseguire il refresh del token: " . $refreshEx->getMessage());
|
||||
}
|
||||
// Ritarda leggermente prima del retry
|
||||
usleep(500000); // 500ms
|
||||
continue;
|
||||
}
|
||||
// Altri errori non gestiti dal retry
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
throw new Exception("Massimo numero di tentativi raggiunto per $endpoint");
|
||||
}
|
||||
|
||||
// Cache file (1 hour TTL)
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (file_exists($cacheFile) && (time() - filemtime($cacheFile) < 3600)) {
|
||||
readfile($cacheFile);
|
||||
exit;
|
||||
}
|
||||
|
||||
// Esegui la chiamata con retry
|
||||
$data = makeApiRequest($api, $endpoint);
|
||||
|
||||
$json = json_encode($data);
|
||||
if (!is_dir(__DIR__ . '/cache')) mkdir(__DIR__ . '/cache', 0777, true);
|
||||
file_put_contents($cacheFile, $json);
|
||||
|
||||
echo $json;
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
$errorResponse = [
|
||||
'error' => $e->getMessage(),
|
||||
'file' => $e->getFile(),
|
||||
'line' => $e->getLine(),
|
||||
'trace' => $e->getTraceAsString()
|
||||
];
|
||||
error_log("Errore in get_clienti.php: " . json_encode($errorResponse));
|
||||
echo json_encode($errorResponse);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
<?php
|
||||
require_once 'include/headscript.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(['error' => 'Invalid request method']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$template_id = isset($input['template_id']) ? (int)$input['template_id'] : 0;
|
||||
$code = isset($input['code']) ? trim($input['code']) : '';
|
||||
|
||||
if ($template_id <= 0) {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing or invalid template_id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($code === '') {
|
||||
http_response_code(400);
|
||||
echo json_encode(['error' => 'Missing code']);
|
||||
exit;
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: Replace this block with your real API call.
|
||||
* Expected response for import_json.php:
|
||||
* {
|
||||
* "success": true,
|
||||
* "reference": "CODE123",
|
||||
* "json": { ... real JSON payload ... }
|
||||
* }
|
||||
*/
|
||||
|
||||
$sampleJson = [
|
||||
'data' => [[
|
||||
'type' => 'trf_data_request',
|
||||
'id' => $code,
|
||||
'attributes' => [
|
||||
'trf_type' => 'apparel',
|
||||
'service_required' => 'regular',
|
||||
'submitter_information' => [
|
||||
'submitter_type' => 'supplier'
|
||||
]
|
||||
]
|
||||
]]
|
||||
];
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'reference' => $code,
|
||||
'json' => $sampleJson
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -1,143 +0,0 @@
|
||||
<?php
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/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();
|
||||
|
||||
$idCliente = isset($_GET['id_cliente']) ? (int)$_GET['id_cliente'] : 0;
|
||||
$limit = isset($_GET['limit']) ? (int)$_GET['limit'] : 3;
|
||||
$signedStatus = trim($_GET['signed_status'] ?? 'all');
|
||||
|
||||
if ($idCliente <= 0) {
|
||||
throw new Exception("Parametro id_cliente mancante o non valido.");
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowed limits only.
|
||||
* This prevents risky wide queries on the live LIMS.
|
||||
*/
|
||||
$allowedLimits = [1, 3, 5, 10];
|
||||
|
||||
if (!in_array($limit, $allowedLimits, true)) {
|
||||
$limit = 3;
|
||||
}
|
||||
|
||||
/*
|
||||
* Allowed signature filters.
|
||||
*/
|
||||
$allowedSignedStatuses = ['all', 'signed', 'not_signed'];
|
||||
|
||||
if (!in_array($signedStatus, $allowedSignedStatuses, true)) {
|
||||
$signedStatus = 'all';
|
||||
}
|
||||
|
||||
/*
|
||||
* Base filter by customer.
|
||||
* We already verified that Rapporto can expand Cliente and returns Cliente.IdCliente.
|
||||
*/
|
||||
$filters = [
|
||||
"Cliente/IdCliente eq {$idCliente}"
|
||||
];
|
||||
|
||||
if ($signedStatus === 'signed') {
|
||||
$filters[] = "Firmato eq true";
|
||||
}
|
||||
|
||||
if ($signedStatus === 'not_signed') {
|
||||
$filters[] = "Firmato eq false";
|
||||
}
|
||||
|
||||
$filter = implode(' and ', $filters);
|
||||
|
||||
/*
|
||||
* Important:
|
||||
* - $top limits the number of reports.
|
||||
* - $orderby=Data desc gets the latest reports first.
|
||||
* - $expand=RapportiFiles retrieves only the PDF file metadata, not the binary PDF.
|
||||
*/
|
||||
$params = [
|
||||
'$filter' => $filter,
|
||||
'$select' => 'IdRapporto,CodiceRapporto,Data,Versione,Firmato,DataStampa',
|
||||
'$expand' => 'RapportiFiles',
|
||||
'$orderby' => 'Data desc',
|
||||
'$top' => $limit
|
||||
];
|
||||
|
||||
$endpoint = "Rapporto?" . http_build_query($params);
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . '/last_rapporti_cliente_endpoint.txt',
|
||||
'[' . date('Y-m-d H:i:s') . '] ' . $endpoint . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
$data = $api->get($endpoint);
|
||||
|
||||
$items = $data['value'] ?? [];
|
||||
|
||||
if (!is_array($items)) {
|
||||
$items = [];
|
||||
}
|
||||
|
||||
$reports = [];
|
||||
|
||||
foreach ($items as $item) {
|
||||
$rapportiFiles = $item['RapportiFiles'] ?? [];
|
||||
$pdfFiles = [];
|
||||
|
||||
if (is_array($rapportiFiles)) {
|
||||
foreach ($rapportiFiles as $file) {
|
||||
$idRapportoFile = intval($file['IdRapportoFile'] ?? 0);
|
||||
|
||||
if ($idRapportoFile > 0) {
|
||||
$pdfFiles[] = [
|
||||
'id_rapporto_file' => $idRapportoFile,
|
||||
'file_name' => $file['FileName'] ?? null,
|
||||
'categoria' => $file['Categoria'] ?? null,
|
||||
'tipo_rapporto' => $file['TipoRapporto'] ?? null,
|
||||
'download_url' => "download_rapporto_pdf.php?id_rapporto_file={$idRapportoFile}"
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$reports[] = [
|
||||
'id_rapporto' => $item['IdRapporto'] ?? null,
|
||||
'codice_rapporto' => $item['CodiceRapporto'] ?? null,
|
||||
'data' => $item['Data'] ?? null,
|
||||
'data_stampa' => $item['DataStampa'] ?? null,
|
||||
'versione' => $item['Versione'] ?? null,
|
||||
'firmato' => $item['Firmato'] ?? null,
|
||||
'pdf_files' => $pdfFiles
|
||||
];
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'id_cliente' => $idCliente,
|
||||
'limit' => $limit,
|
||||
'signed_status' => $signedStatus,
|
||||
'endpoint' => $endpoint,
|
||||
'count' => count($reports),
|
||||
'reports' => $reports
|
||||
], 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') . ' - get_rapporti_cliente.php - ' . $e->getMessage() . PHP_EOL,
|
||||
FILE_APPEND
|
||||
);
|
||||
|
||||
http_response_code(500);
|
||||
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'error' => $e->getMessage()
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
||||
}
|
||||
@@ -17,7 +17,6 @@ try {
|
||||
// rapporto_by_codice_expand_step.php?codice=2541111&step=files_campioni
|
||||
|
||||
$codiceRapporto = trim($_GET['codice'] ?? '');
|
||||
// Safe step mode: default is base, but allows controlled read-only steps
|
||||
$step = trim($_GET['step'] ?? 'base');
|
||||
|
||||
if ($codiceRapporto === '') {
|
||||
@@ -26,9 +25,10 @@ try {
|
||||
|
||||
$allowedSteps = [
|
||||
'base' => '',
|
||||
'files' => 'RapportiFiles',
|
||||
'allegati' => 'RapportiAllegati',
|
||||
'campioni' => 'CampioniDatiRapporto',
|
||||
'files' => 'RapportiFiles,Cliente',
|
||||
'cliente' => 'Cliente'
|
||||
'files_campioni' => 'RapportiFiles,CampioniDatiRapporto'
|
||||
];
|
||||
|
||||
if (!array_key_exists($step, $allowedSteps)) {
|
||||
@@ -37,8 +37,7 @@ try {
|
||||
|
||||
// Escape OData per eventuali apostrofi
|
||||
$codiceRapportoSafe = str_replace("'", "''", $codiceRapporto);
|
||||
// Safe version of codice rapporto for filenames
|
||||
$codiceRapportoFileSafe = preg_replace('/[^a-zA-Z0-9_-]/', '_', $codiceRapporto);
|
||||
|
||||
/*
|
||||
* STEP 1 - Trova IdRapporto partendo da CodiceRapporto.
|
||||
* Query leggera, con $select e $top=1.
|
||||
@@ -108,43 +107,15 @@ try {
|
||||
|
||||
$detailData = $api->get($detailEndpoint);
|
||||
|
||||
$pdfFiles = [];
|
||||
|
||||
if ($step === 'files') {
|
||||
$rapportiFiles = $detailData['RapportiFiles'] ?? [];
|
||||
|
||||
if (is_array($rapportiFiles)) {
|
||||
foreach ($rapportiFiles as $file) {
|
||||
$idRapportoFile = intval($file['IdRapportoFile'] ?? 0);
|
||||
|
||||
if ($idRapportoFile > 0) {
|
||||
$pdfFiles[] = [
|
||||
'id_rapporto_file' => $idRapportoFile,
|
||||
'file_name' => $file['FileName'] ?? null,
|
||||
'categoria' => $file['Categoria'] ?? null,
|
||||
'tipo_rapporto' => $file['TipoRapporto'] ?? null,
|
||||
'download_endpoint' => "MediaFile/DownloadStream?objectType=RapportoFile&propertyName=FileContent&objectKey={$idRapportoFile}"
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$clienteData = null;
|
||||
|
||||
if ($step === 'cliente' || $step === 'files') {
|
||||
$clienteData = $detailData['Cliente'] ?? null;
|
||||
}
|
||||
|
||||
file_put_contents(
|
||||
__DIR__ . "/rapporto_codice_{$codiceRapportoFileSafe}_{$step}.json",
|
||||
__DIR__ . "/rapporto_codice_{$codiceRapportoSafe}_{$step}.json",
|
||||
json_encode([
|
||||
'search' => $searchData,
|
||||
'detail' => $detailData
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)
|
||||
);
|
||||
|
||||
$response = [
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'codice_rapporto' => $codiceRapporto,
|
||||
'id_rapporto' => $rapportoId,
|
||||
@@ -153,17 +124,7 @@ try {
|
||||
'detail_endpoint' => $detailEndpoint,
|
||||
'rapporto_base' => $rapportoBase,
|
||||
'data' => $detailData
|
||||
];
|
||||
|
||||
if ($step === 'files') {
|
||||
$response['pdf_files'] = $pdfFiles;
|
||||
}
|
||||
|
||||
if ($step === 'cliente' || $step === 'files') {
|
||||
$response['cliente'] = $clienteData;
|
||||
}
|
||||
|
||||
echo json_encode($response, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
], JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
|
||||
} catch (Exception $e) {
|
||||
file_put_contents(
|
||||
__DIR__ . '/error_log.txt',
|
||||
|
||||
@@ -66,7 +66,11 @@
|
||||
// ── Client data (AJAX Select2, no bulk loading) ──────────────────────
|
||||
|
||||
function formatClientLabel(client) {
|
||||
const nome = client.Nominativo || client.text || "Nome non disponibile";
|
||||
const nome = (
|
||||
client.Nominativo ||
|
||||
client.text ||
|
||||
"Nome non disponibile"
|
||||
).trim();
|
||||
const id = client.IdCliente || client.id || "";
|
||||
const codiceCliente = (
|
||||
client.CodiceCliente ??
|
||||
@@ -75,11 +79,14 @@
|
||||
)
|
||||
.toString()
|
||||
.trim();
|
||||
|
||||
// Lettera estratta (parte dopo "_", altrimenti prima lettera del codice)
|
||||
const suffix = (codiceCliente.split("_")[1] || "").trim();
|
||||
const shortCode =
|
||||
suffix || (codiceCliente ? codiceCliente.charAt(0) : "--");
|
||||
|
||||
return `${nome.trim()} - ${shortCode} (ID: ${id})`;
|
||||
// Nome - I - 123
|
||||
return `${nome} - ${shortCode} - ${id}`;
|
||||
}
|
||||
|
||||
// Cache of resolved client names: id → name
|
||||
@@ -110,6 +117,9 @@
|
||||
ids.add(String(val));
|
||||
}
|
||||
}
|
||||
if (row.fixedFields && row.fixedFields.ClienteFatturazione) {
|
||||
ids.add(String(row.fixedFields.ClienteFatturazione));
|
||||
}
|
||||
});
|
||||
|
||||
// Batch resolve via single request per ID
|
||||
@@ -156,33 +166,50 @@
|
||||
return textA.localeCompare(textB, "it", { sensitivity: "base" });
|
||||
});
|
||||
}
|
||||
// Select2 AJAX config for client selects
|
||||
const clientSelect2Config = {
|
||||
placeholder: "Search client...",
|
||||
allowClear: true,
|
||||
width: "100%",
|
||||
minimumInputLength: 0,
|
||||
sorter: sortSelect2ResultsByStart,
|
||||
dropdownCssClass: "select2-dropdown-smaller",
|
||||
ajax: {
|
||||
url: "search_clienti.php",
|
||||
dataType: "json",
|
||||
delay: 150,
|
||||
data: function (params) {
|
||||
return { q: params.term || "", limit: 20 };
|
||||
},
|
||||
processResults: function (data) {
|
||||
const results = (data.results || []).map((item) => ({
|
||||
...item,
|
||||
text: formatClientLabel(item),
|
||||
}));
|
||||
|
||||
return { results: results };
|
||||
},
|
||||
cache: true,
|
||||
},
|
||||
// Mappa: quale filtro Inattivo applicare per ogni campo cliente
|
||||
const clientFieldTipoMap = {
|
||||
ClienteFornitore: "attivo",
|
||||
ClienteAnalisi: "inattivo",
|
||||
ClienteFatturazione: "attivo",
|
||||
};
|
||||
|
||||
// Select2 AJAX config factory for client selects (parametrizzata su "tipo")
|
||||
function clientSelect2ConfigFor(tipo) {
|
||||
return {
|
||||
placeholder: "Search client...",
|
||||
allowClear: true,
|
||||
width: "100%",
|
||||
minimumInputLength: 0,
|
||||
sorter: sortSelect2ResultsByStart,
|
||||
dropdownCssClass: "select2-dropdown-smaller",
|
||||
ajax: {
|
||||
url: "search_clienti.php",
|
||||
dataType: "json",
|
||||
delay: 150,
|
||||
data: function (params) {
|
||||
const d = { q: params.term || "", limit: 20 };
|
||||
if (tipo) d.tipo = tipo;
|
||||
return d;
|
||||
},
|
||||
processResults: function (data) {
|
||||
const results = (data.results || []).map((item) => ({
|
||||
...item,
|
||||
text: formatClientLabel(item),
|
||||
}));
|
||||
|
||||
return { results: results };
|
||||
},
|
||||
cache: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// Helper: inizializza Select2 su un elemento leggendo il tipo da data-client-tipo
|
||||
function initClientSelect2(el) {
|
||||
const tipo = el.dataset.clientTipo || "";
|
||||
$(el).select2(clientSelect2ConfigFor(tipo));
|
||||
}
|
||||
|
||||
async function loadClientData() {
|
||||
// No longer loads all clients — AJAX Select2 handles search
|
||||
// Just resolve names for pre-selected values
|
||||
@@ -215,6 +242,7 @@
|
||||
getParams: (cid) => ({ id_cliente: cid }),
|
||||
},
|
||||
ClienteFornitore: { source: "clients" },
|
||||
ClienteFatturazione: { source: "clients" },
|
||||
ClienteAnalisi: { source: "clients" },
|
||||
};
|
||||
|
||||
@@ -536,6 +564,7 @@
|
||||
sel.className =
|
||||
"cell-input dropdown-select client-select searchable-client fornitore-select";
|
||||
sel.dataset.currentValue = row.cliente_fornitore_id || "";
|
||||
sel.dataset.clientTipo = "attivo";
|
||||
sel.innerHTML = buildClientOptionsHTML(
|
||||
row.cliente_fornitore_id,
|
||||
);
|
||||
@@ -631,12 +660,13 @@
|
||||
if (config && config.source === "clients") {
|
||||
const reqCls = col.isRequired ? " required-input" : "";
|
||||
const req = col.isRequired ? " required" : "";
|
||||
const tipo = clientFieldTipoMap[col.key] || "";
|
||||
let opts = '<option value="">Select...</option>';
|
||||
if (value) {
|
||||
const label = clientNameCache[value] || value;
|
||||
opts += `<option value="${esc(String(value))}" selected>${esc(String(label))}</option>`;
|
||||
}
|
||||
return `<select class="cell-input manual-input fixed-input searchable-client api-fixed-select${reqCls}" data-fixed-key="${escAttr(col.key)}" data-current-value="${escAttr(value)}"${req}>${opts}</select>`;
|
||||
return `<select class="cell-input manual-input fixed-input searchable-client api-fixed-select${reqCls}" data-fixed-key="${escAttr(col.key)}" data-current-value="${escAttr(value)}" data-client-tipo="${escAttr(tipo)}"${req}>${opts}</select>`;
|
||||
}
|
||||
|
||||
// Select — build from cache
|
||||
@@ -679,9 +709,7 @@
|
||||
div.style.position = "relative";
|
||||
|
||||
let html = "";
|
||||
if (meta.isAdmin) {
|
||||
html += `<button type="button" class="export-lims-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="${isExported ? "Already exported" : "Export to LIMS"}" style="background:${isExported ? "#ccc" : "#eb0b0b"}; color:white; border:none; border-radius:5px; cursor:${isExported ? "not-allowed" : "pointer"}; ${isExported ? "opacity:0.5;" : ""}" ${isExported ? "disabled" : ""}><i class="fas fa-upload"></i></button>`;
|
||||
}
|
||||
html += `<button type="button" class="export-lims-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="${isExported ? "Already exported" : "Export to LIMS"}" style="background:${isExported ? "#ccc" : "#eb0b0b"}; color:white; border:none; border-radius:5px; cursor:${isExported ? "not-allowed" : "pointer"}; ${isExported ? "opacity:0.5;" : ""}" ${isExported ? "disabled" : ""}><i class="fas fa-upload"></i></button>`;
|
||||
html += `<button type="button" class="save-btn action-btn" data-row="${rowIndex}" title="Save" style="background:#28a745; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-save"></i></button>`;
|
||||
html += `<button type="button" class="photos-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="Photos" style="background:#007bff; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-camera"></i></button>`;
|
||||
html += `<button type="button" class="parts-btn action-btn" data-row="${rowIndex}" data-iddatadb="${row.iddatadb}" title="Parts" style="background:#ffc107; color:white; border:none; border-radius:5px; cursor:pointer;"><i class="fas fa-puzzle-piece"></i></button>`;
|
||||
@@ -1072,7 +1100,7 @@
|
||||
`<button type="button" class="propagate-btn" data-column="idclient" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else if (col.type === "cliente_fornitore_id") {
|
||||
cell.innerHTML =
|
||||
`<select class="custom-field dropdown-select client-select searchable-client" data-column="cliente_fornitore_id" id="clienteFornitoreSelect"><option value="">Select a supplier...</option></select>` +
|
||||
`<select class="custom-field dropdown-select client-select searchable-client" data-column="cliente_fornitore_id" data-client-tipo="attivo" id="clienteFornitoreSelect"><option value="">Select a supplier...</option></select>` +
|
||||
`<button type="button" class="propagate-btn" data-column="cliente_fornitore_id" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else if (col.type === "fixed" && col.dataType === "DATE") {
|
||||
cell.innerHTML =
|
||||
@@ -1081,8 +1109,14 @@
|
||||
} else if (col.type === "fixed") {
|
||||
const isApiField = !!fixedFieldApiConfig[col.key];
|
||||
if (isApiField) {
|
||||
const tipo = clientFieldTipoMap[col.key] || "";
|
||||
const isClientSourced =
|
||||
fixedFieldApiConfig[col.key]?.source === "clients";
|
||||
const clientCls = isClientSourced
|
||||
? " client-select searchable-client"
|
||||
: "";
|
||||
cell.innerHTML =
|
||||
`<select class="custom-field dropdown-select api-fixed-select fixed-top" data-column="fixed_${col.key}" data-fixed-key="${col.key}"><option value="">Seleziona...</option></select>` +
|
||||
`<select class="custom-field dropdown-select api-fixed-select fixed-top${clientCls}" data-column="fixed_${col.key}" data-fixed-key="${col.key}" data-client-tipo="${tipo}"><option value="">Seleziona...</option></select>` +
|
||||
`<button type="button" class="propagate-btn" data-column="fixed_${col.key}" data-col-index="${colIdx}"><i class="fas fa-arrow-down"></i></button>`;
|
||||
} else {
|
||||
cell.innerHTML =
|
||||
@@ -1117,12 +1151,12 @@
|
||||
const clientSel = document.getElementById("clientSelect");
|
||||
if (clientSel) {
|
||||
clientSel.innerHTML = buildClientOptionsHTML(meta.defaultIdclient);
|
||||
$(clientSel).select2(clientSelect2Config);
|
||||
initClientSelect2(clientSel);
|
||||
}
|
||||
const fornitSel = document.getElementById("clienteFornitoreSelect");
|
||||
if (fornitSel) {
|
||||
fornitSel.innerHTML = buildClientOptionsHTML("");
|
||||
$(fornitSel).select2(clientSelect2Config);
|
||||
initClientSelect2(fornitSel);
|
||||
}
|
||||
|
||||
// Fixed field selects in top row
|
||||
@@ -1132,7 +1166,7 @@
|
||||
|
||||
// Client-sourced → init as AJAX Select2
|
||||
if (config && config.source === "clients") {
|
||||
$(sel).select2(clientSelect2Config);
|
||||
initClientSelect2(sel);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1585,7 +1619,7 @@
|
||||
$(this)
|
||||
.find(".searchable-client:not(.select2-hidden-accessible)")
|
||||
.each(function () {
|
||||
$(this).select2(clientSelect2Config);
|
||||
initClientSelect2(this);
|
||||
});
|
||||
$(this)
|
||||
.find(".searchable-dropdown:not(.select2-hidden-accessible)")
|
||||
|
||||
@@ -130,10 +130,60 @@
|
||||
color: #6c757d;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #6c757d;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
width: 100%;
|
||||
margin-top: 6px;
|
||||
margin-bottom: 2px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
.section-title:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.template-btn-wrapper {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.favorite-star {
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
right: -6px;
|
||||
width: 22px;
|
||||
height: 22px;
|
||||
background: #fff;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.25);
|
||||
font-size: 13px;
|
||||
color: #ccc;
|
||||
z-index: 2;
|
||||
transition: color 0.15s, transform 0.15s;
|
||||
}
|
||||
|
||||
.favorite-star:hover {
|
||||
transform: scale(1.15);
|
||||
}
|
||||
|
||||
.favorite-star.active {
|
||||
color: #ffc107;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<?php include('include/popup.php'); ?>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
@@ -231,24 +281,23 @@
|
||||
}
|
||||
}
|
||||
|
||||
function createButton(template) {
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
}
|
||||
|
||||
function createButtonWrapper(template) {
|
||||
const sizeClass = getSizeClass(template.button_size);
|
||||
const sourceType = (template.source_type || '').toUpperCase();
|
||||
const iconClass = getTemplateIcon(sourceType);
|
||||
const isFavorite = !!parseInt(template.is_favorite);
|
||||
|
||||
const wrapper = document.createElement("div");
|
||||
wrapper.className = "template-btn-wrapper";
|
||||
|
||||
const btn = document.createElement("a");
|
||||
|
||||
// Redirect based on template source type
|
||||
if (sourceType === 'XLS') {
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
} else if (sourceType === 'API' || sourceType === 'JSON') {
|
||||
btn.href = `import_json.php?id=${template.id}`;
|
||||
} else if (sourceType === 'PDF') {
|
||||
btn.href = `import_pdf.php?id=${template.id}`;
|
||||
} else {
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
}
|
||||
|
||||
btn.href = `import_xls2.php?id=${template.id}`;
|
||||
btn.className = `btn ${sizeClass}`;
|
||||
btn.style.backgroundColor = template.button_bg_color || '#0d6efd';
|
||||
btn.style.color = template.button_text_color || '#ffffff';
|
||||
@@ -256,17 +305,62 @@
|
||||
btn.setAttribute("data-source-type", sourceType);
|
||||
|
||||
btn.innerHTML = `
|
||||
<i class="${iconClass} template-icon"></i>
|
||||
<span>${escapeHtml(template.button_label || 'Unnamed')}</span>
|
||||
`;
|
||||
<i class="${iconClass} template-icon"></i>
|
||||
<span>${escapeHtml(template.button_label || 'Unnamed')}</span>
|
||||
`;
|
||||
|
||||
return btn;
|
||||
const star = document.createElement("span");
|
||||
star.className = "favorite-star" + (isFavorite ? " active" : "");
|
||||
star.innerHTML = `<i class="bx ${isFavorite ? 'bxs-star' : 'bx-star'}"></i>`;
|
||||
star.setAttribute("data-template-id", template.id);
|
||||
star.title = isFavorite ? "Rimuovi dai preferiti" : "Aggiungi ai preferiti";
|
||||
|
||||
star.addEventListener("click", function(e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
toggleFavorite(template.id);
|
||||
});
|
||||
|
||||
wrapper.appendChild(btn);
|
||||
wrapper.appendChild(star);
|
||||
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const div = document.createElement('div');
|
||||
div.textContent = text;
|
||||
return div.innerHTML;
|
||||
function toggleFavorite(templateId) {
|
||||
const template = allTemplates.find(t => String(t.id) === String(templateId));
|
||||
if (!template) return;
|
||||
|
||||
const newState = !parseInt(template.is_favorite);
|
||||
|
||||
// Optimistic update
|
||||
template.is_favorite = newState ? 1 : 0;
|
||||
renderTemplates(document.getElementById("searchInput").value.toLowerCase().trim());
|
||||
|
||||
fetch("toggle_template_favorite.php", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: templateId,
|
||||
is_favorite: newState
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("Failed to toggle favorite:", data.message);
|
||||
// Rollback in caso di errore
|
||||
template.is_favorite = newState ? 0 : 1;
|
||||
renderTemplates(document.getElementById("searchInput").value.toLowerCase().trim());
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("Fetch error toggling favorite:", error);
|
||||
template.is_favorite = newState ? 0 : 1;
|
||||
renderTemplates(document.getElementById("searchInput").value.toLowerCase().trim());
|
||||
});
|
||||
}
|
||||
|
||||
function clearContainers() {
|
||||
@@ -275,6 +369,26 @@
|
||||
});
|
||||
}
|
||||
|
||||
function renderSection(container, title, templates) {
|
||||
if (templates.length === 0) return;
|
||||
|
||||
const heading = document.createElement("div");
|
||||
heading.className = "section-title";
|
||||
heading.textContent = title;
|
||||
container.appendChild(heading);
|
||||
|
||||
const group = document.createElement("div");
|
||||
group.className = "template-buttons";
|
||||
group.style.marginTop = "0";
|
||||
group.style.paddingTop = "0";
|
||||
|
||||
templates.forEach(template => {
|
||||
group.appendChild(createButtonWrapper(template));
|
||||
});
|
||||
|
||||
container.appendChild(group);
|
||||
}
|
||||
|
||||
function renderTemplates(searchValue = '') {
|
||||
clearContainers();
|
||||
|
||||
@@ -306,9 +420,11 @@
|
||||
return;
|
||||
}
|
||||
|
||||
grouped[type].forEach(template => {
|
||||
container.appendChild(createButton(template));
|
||||
});
|
||||
const favorites = grouped[type].filter(t => parseInt(t.is_favorite) === 1);
|
||||
const others = grouped[type].filter(t => parseInt(t.is_favorite) !== 1);
|
||||
|
||||
renderSection(container, "★ Preferiti", favorites);
|
||||
renderSection(container, "Altri", others);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ if (!file_exists(__DIR__ . '/import_debug.log')) {
|
||||
error_log("Inizio importazione alle " . date('Y-m-d H:i:s'));
|
||||
|
||||
include('include/headscript.php');
|
||||
require_once(__DIR__ . '/class/binding-functions.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"));
|
||||
@@ -26,7 +25,6 @@ $rows = json_decode(urldecode($_POST['rows'] ?? '[]'), true);
|
||||
$excelrows = json_decode(urldecode($_POST['excelrows'] ?? '[]'), true);
|
||||
|
||||
$newFilename = $_POST['filename'];
|
||||
$source_type = strtolower(trim($_POST['source_type'] ?? 'xls'));
|
||||
|
||||
$_SESSION['template_id'] = $template_id;
|
||||
$_SESSION['selected_rows'] = $selected_rows;
|
||||
@@ -39,10 +37,32 @@ error_log("Received Data - Template ID: $template_id, Selected Rows: " . json_en
|
||||
error_log("Columns: " . json_encode($columns));
|
||||
error_log("Rows: " . json_encode($rows));
|
||||
error_log("Excelrows: " . json_encode($excelrows));
|
||||
error_log("Source type: " . $source_type);
|
||||
|
||||
$user_id = $iduserlogin ?? 1;
|
||||
|
||||
/**
|
||||
* Normalizza un nome colonna per il confronto header Excel <-> mapping.
|
||||
* Risolve alla radice i mismatch causati da caratteri invisibili:
|
||||
* - non-breaking space (U+00A0), narrow NBSP (U+202F), figure space (U+2007), ecc. -> spazio normale
|
||||
* - tab, newline e altri whitespace -> spazio normale
|
||||
* - spazi multipli consecutivi -> un solo spazio
|
||||
* - trim ai bordi
|
||||
* NB: usata SOLO per il matching, non altera i valori importati.
|
||||
*/
|
||||
function normalizeColName($s): string
|
||||
{
|
||||
$s = (string)$s;
|
||||
// Converte i principali spazi Unicode "insidiosi" in spazio normale
|
||||
$s = str_replace(
|
||||
["\xC2\xA0", "\xE2\x80\xAF", "\xE2\x80\x87", "\xE2\x80\x89", "\xE2\x80\x8A", "\xEF\xBB\xBF"],
|
||||
' ',
|
||||
$s
|
||||
);
|
||||
// Qualsiasi sequenza di whitespace (spazi, tab, newline) -> singolo spazio
|
||||
$s = preg_replace('/\s+/u', ' ', $s);
|
||||
return trim((string)$s);
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
@@ -50,23 +70,7 @@ $pdo = $db->getConnection();
|
||||
$importReferenceCode = date('YmdHis') . '-' . uniqid();
|
||||
|
||||
// Recupera tutti i mapping dal template
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
id,
|
||||
excel_column,
|
||||
json_node,
|
||||
data_type,
|
||||
is_required,
|
||||
manual_default,
|
||||
is_manual,
|
||||
field_label,
|
||||
field_id,
|
||||
main_field,
|
||||
auto_value,
|
||||
has_list
|
||||
FROM template_mapping
|
||||
WHERE template_id = ?
|
||||
");
|
||||
$stmt = $pdo->prepare("SELECT id, excel_column, data_type, is_required, manual_default, is_manual, field_label, field_id, main_field, auto_value FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$template_id]);
|
||||
$allMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
@@ -84,41 +88,14 @@ foreach ($allMappings as $mapping) {
|
||||
}
|
||||
}
|
||||
|
||||
// Campi fixed mappati da JSON (default_source='json') per questo template.
|
||||
$fixedJsonFields = [];
|
||||
if ($source_type === 'json') {
|
||||
$fxStmt = $pdo->prepare("
|
||||
SELECT fixed_field_key, json_node, data_type
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ? AND default_source = 'json' AND json_node IS NOT NULL AND json_node <> ''
|
||||
");
|
||||
$fxStmt->execute([$template_id]);
|
||||
$fixedJsonFields = $fxStmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
}
|
||||
|
||||
// Inserisci le righe selezionate in datadb
|
||||
$insertedIds = [];
|
||||
|
||||
// Binding JSON -> LIMS senza corrispondenza salvata, per "kind:key|json_value".
|
||||
$pendingBindings = [];
|
||||
// Binding risolti in automatico durante questo import (solo per visualizzazione).
|
||||
$autoBindings = [];
|
||||
// Binding gia' salvati in precedenza, usati su questo import (visualizzazione + modifica).
|
||||
$savedBindings = [];
|
||||
foreach ($selected_rows as $loopIndex => $rowIndex) {
|
||||
|
||||
if ($source_type === 'json') {
|
||||
// JSON import sends only selected rows in rows/excelrows
|
||||
$row = $rows[$loopIndex] ?? null;
|
||||
$excelrow = $excelrows[$loopIndex] ?? ('JSON-' . ($loopIndex + 1));
|
||||
} else {
|
||||
// XLS import keeps original row indexes
|
||||
$row = $rows[$rowIndex] ?? null;
|
||||
$excelrow = $excelrows[$rowIndex] ?? null;
|
||||
}
|
||||
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. Source type: $source_type, loopIndex: $loopIndex, rowIndex: $rowIndex");
|
||||
error_log("Errore: riga o excelrow mancante per rowIndex $rowIndex");
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -128,14 +105,6 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
|
||||
$template = $template_stmt->fetch(PDO::FETCH_ASSOC);
|
||||
$default_idclient = $template['idclient'] ?? null;
|
||||
|
||||
// excelrow e' INT: dal JSON arriva tipo 'JSON-1', tengo solo la parte numerica.
|
||||
if (is_numeric($excelrow)) {
|
||||
$excelrowDb = (int) $excelrow;
|
||||
} else {
|
||||
$digits = preg_replace('/\D+/', '', (string) $excelrow);
|
||||
$excelrowDb = $digits !== '' ? (int) $digits : ($loopIndex + 1);
|
||||
}
|
||||
|
||||
$values = [
|
||||
$template_id,
|
||||
$importReferenceCode,
|
||||
@@ -144,7 +113,7 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
|
||||
$user_id,
|
||||
null,
|
||||
date('Y-m-d'),
|
||||
$excelrowDb,
|
||||
$excelrow,
|
||||
$default_idclient // Aggiungi idclient
|
||||
];
|
||||
$sql = "INSERT INTO datadb (templateid, importreferencecode, filename_import, status, user_id, limscode, importdate, excelrow, idclient) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
@@ -158,72 +127,19 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
|
||||
foreach ($allMappings as $mapping) {
|
||||
$fieldValue = null;
|
||||
if (!$mapping['is_manual']) {
|
||||
$sourceColumn = '';
|
||||
|
||||
if ($source_type === 'json') {
|
||||
$sourceColumn = trim($mapping['json_node'] ?? '');
|
||||
} else {
|
||||
$sourceColumn = trim($mapping['excel_column'] ?? '');
|
||||
}
|
||||
|
||||
// Fallback: if JSON node is empty, try excel_column
|
||||
if ($sourceColumn === '') {
|
||||
$sourceColumn = trim($mapping['excel_column'] ?? '');
|
||||
}
|
||||
|
||||
$columnsTrimmed = array_map('trim', $columns);
|
||||
|
||||
$candidateColumns = [];
|
||||
|
||||
if ($sourceColumn !== '') {
|
||||
$candidateColumns[] = $sourceColumn;
|
||||
|
||||
if ($source_type === 'json') {
|
||||
// Common JSON path variants
|
||||
$candidateColumns[] = preg_replace('/^data\[\]\./', '', $sourceColumn);
|
||||
$candidateColumns[] = preg_replace('/^data\.0\./', '', $sourceColumn);
|
||||
$candidateColumns[] = str_replace('data[].', 'data.0.', $sourceColumn);
|
||||
$candidateColumns[] = str_replace('data.0.', 'data[].', $sourceColumn);
|
||||
}
|
||||
}
|
||||
|
||||
// Remove empty and duplicate candidates
|
||||
$candidateColumns = array_values(array_unique(array_filter($candidateColumns, function ($value) {
|
||||
return trim((string)$value) !== '';
|
||||
})));
|
||||
|
||||
$sourceColumnIndex = false;
|
||||
$matchedColumn = '';
|
||||
|
||||
foreach ($candidateColumns as $candidateColumn) {
|
||||
$candidateColumn = trim($candidateColumn);
|
||||
$index = array_search($candidateColumn, $columnsTrimmed);
|
||||
|
||||
if ($index !== false) {
|
||||
$sourceColumnIndex = $index;
|
||||
$matchedColumn = $candidateColumn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($sourceColumnIndex !== false && isset($row[$sourceColumnIndex]) && $row[$sourceColumnIndex] !== '') {
|
||||
$fieldValue = $row[$sourceColumnIndex];
|
||||
|
||||
error_log(
|
||||
"Found source column. Original: '$sourceColumn', Matched: '$matchedColumn', Index: $sourceColumnIndex, Value: " .
|
||||
var_export($fieldValue, true)
|
||||
);
|
||||
$excelColumn = trim($mapping['excel_column']);
|
||||
// Match robusto: normalizza header e mapping (NBSP, spazi doppi, tab...)
|
||||
// così un solo/doppio spazio o un non-breaking space non rompono il confronto.
|
||||
$excelColumnIndex = array_search(
|
||||
normalizeColName($excelColumn),
|
||||
array_map('normalizeColName', $columns)
|
||||
);
|
||||
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(
|
||||
"Source column not found or empty. Original: '$sourceColumn'. Candidates: " .
|
||||
json_encode($candidateColumns) .
|
||||
". Available columns: " .
|
||||
json_encode($columnsTrimmed) .
|
||||
". Using default: " .
|
||||
var_export($fieldValue, true)
|
||||
);
|
||||
error_log("Excel column '$excelColumn' not found or empty, using default: " . var_export($fieldValue, true));
|
||||
}
|
||||
switch ($mapping['data_type']) {
|
||||
case 'INT':
|
||||
@@ -256,215 +172,70 @@ foreach ($selected_rows as $loopIndex => $rowIndex) {
|
||||
}
|
||||
}
|
||||
|
||||
// Binding JSON -> LIMS solo per i campi a lista importati da JSON.
|
||||
if (
|
||||
$source_type === 'json'
|
||||
&& !$mapping['is_manual']
|
||||
&& binding_is_list_field($mapping)
|
||||
&& $fieldValue !== null
|
||||
&& $fieldValue !== ''
|
||||
) {
|
||||
$jsonValue = (string) $fieldValue;
|
||||
$existing = binding_lookup($pdo, (int) $mapping['id'], $jsonValue);
|
||||
|
||||
if ($existing) {
|
||||
$fieldValue = $existing['lims_value'];
|
||||
|
||||
$key = 'cf:' . $mapping['id'] . '|' . $jsonValue;
|
||||
if (!isset($savedBindings[$key])) {
|
||||
$savedBindings[$key] = [
|
||||
'kind' => 'custom',
|
||||
'mapping_id' => (int) $mapping['id'],
|
||||
'field_id' => (int) $mapping['field_id'],
|
||||
'field_label' => $mapping['field_label'],
|
||||
'json_value' => $jsonValue,
|
||||
'lims_value' => (string) $existing['lims_value'],
|
||||
'lims_value_id' => (int) $existing['lims_value_id'],
|
||||
'datadb_ids' => [],
|
||||
];
|
||||
}
|
||||
$savedBindings[$key]['datadb_ids'][] = (int) $iddatadb;
|
||||
} else {
|
||||
// Nessun binding salvato: provo l'auto-match 1-a-1 sui valori LIMS.
|
||||
$limsValues = binding_get_lims_values((int) $mapping['field_id']);
|
||||
$autoMatch = binding_auto_match($limsValues, $jsonValue);
|
||||
|
||||
if ($autoMatch) {
|
||||
binding_upsert(
|
||||
$pdo,
|
||||
(int) $template_id,
|
||||
(int) $mapping['id'],
|
||||
(int) $mapping['field_id'],
|
||||
$jsonValue,
|
||||
(int) $autoMatch['IdCustomFieldsValue'],
|
||||
(string) $autoMatch['Valore'],
|
||||
$user_id
|
||||
);
|
||||
$fieldValue = (string) $autoMatch['Valore'];
|
||||
|
||||
$key = 'cf:' . $mapping['id'] . '|' . $jsonValue;
|
||||
if (!isset($autoBindings[$key])) {
|
||||
$autoBindings[$key] = [
|
||||
'kind' => 'custom',
|
||||
'mapping_id' => (int) $mapping['id'],
|
||||
'field_id' => (int) $mapping['field_id'],
|
||||
'field_label' => $mapping['field_label'],
|
||||
'json_value' => $jsonValue,
|
||||
'lims_value' => (string) $autoMatch['Valore'],
|
||||
'lims_value_id' => (int) $autoMatch['IdCustomFieldsValue'],
|
||||
'datadb_ids' => [],
|
||||
];
|
||||
}
|
||||
$autoBindings[$key]['datadb_ids'][] = (int) $iddatadb;
|
||||
} else {
|
||||
$key = 'cf:' . $mapping['id'] . '|' . $jsonValue;
|
||||
if (!isset($pendingBindings[$key])) {
|
||||
$pendingBindings[$key] = [
|
||||
'kind' => 'custom',
|
||||
'mapping_id' => (int) $mapping['id'],
|
||||
'field_id' => (int) $mapping['field_id'],
|
||||
'field_label' => $mapping['field_label'],
|
||||
'json_value' => $jsonValue,
|
||||
'datadb_ids' => [],
|
||||
];
|
||||
}
|
||||
$pendingBindings[$key]['datadb_ids'][] = (int) $iddatadb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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'] . ", Source Column: " . ($sourceColumn ?? 'N/A') . ", Source Type: " . $source_type . ", Manual Default: " . ($mapping['manual_default'] ?? 'N/A'));
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Fixed fields mappati da JSON (scrivono colonne datadb) ----
|
||||
if ($source_type === 'json' && !empty($fixedJsonFields)) {
|
||||
$fixedUpdates = []; // colonna datadb => valore (id LIMS o data)
|
||||
// ------------------------------------------------------------
|
||||
// AUTO SET: schema field_id = 244 (Accettatore) con auth_users.lims_user_id
|
||||
// Spostato dal vecchio flusso (import_edit.php) al flusso nuovo.
|
||||
// Autocompila il custom field 244 con il lims_user_id dell'utente loggato.
|
||||
// ------------------------------------------------------------
|
||||
try {
|
||||
// 1) Leggi il lims_user_id dell'utente loggato
|
||||
$stmtUser = $pdo->prepare("SELECT lims_user_id FROM auth_users WHERE id = ? LIMIT 1");
|
||||
$stmtUser->execute([(int)$user_id]);
|
||||
$limsUserId = $stmtUser->fetchColumn();
|
||||
|
||||
foreach ($fixedJsonFields as $fx) {
|
||||
$fixedKey = $fx['fixed_field_key'];
|
||||
$column = binding_fixed_column($fixedKey);
|
||||
if (!$column) {
|
||||
continue;
|
||||
}
|
||||
// normalize
|
||||
$limsUserId = ($limsUserId !== false && $limsUserId !== null && $limsUserId !== '') ? (string)$limsUserId : '';
|
||||
|
||||
$idx = binding_find_column_index((string) $fx['json_node'], $columns);
|
||||
$raw = ($idx >= 0 && isset($row[$idx])) ? trim((string) $row[$idx]) : '';
|
||||
if ($raw === '') {
|
||||
continue;
|
||||
}
|
||||
if ($limsUserId !== '' && !empty($insertedIds)) {
|
||||
|
||||
// Campo non a lista (es. ConsegnaRichiesta DATE): scrivo il valore direttamente.
|
||||
if (!binding_fixed_is_list($fixedKey)) {
|
||||
if (($fx['data_type'] ?? '') === 'DATE') {
|
||||
$fixedUpdates[$column] = date('Y-m-d', strtotime($raw));
|
||||
// 2) Trova il mapping_id per field_id = 244 in questo template
|
||||
$stmtMap = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ? AND field_id = 244 LIMIT 1");
|
||||
$stmtMap->execute([(int)$template_id]);
|
||||
$mappingId244 = (int)$stmtMap->fetchColumn();
|
||||
|
||||
if ($mappingId244 > 0) {
|
||||
|
||||
// 3) Upsert del valore in import_data_details per ogni riga inserita
|
||||
$pdo->beginTransaction();
|
||||
|
||||
$updStmt = $pdo->prepare("UPDATE import_data_details SET field_value = ? WHERE id = ? AND mapping_id = ?");
|
||||
$insStmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
|
||||
|
||||
foreach ($insertedIds as $iddatadb) {
|
||||
$iddatadb = (int)$iddatadb;
|
||||
if ($iddatadb <= 0) continue;
|
||||
|
||||
$updStmt->execute([$limsUserId, $iddatadb, $mappingId244]);
|
||||
|
||||
if ($updStmt->rowCount() === 0) {
|
||||
$insStmt->execute([$iddatadb, $mappingId244, $limsUserId]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// Binding gia' salvato.
|
||||
$existing = binding_lookup_fixed($pdo, (int) $template_id, $fixedKey, $raw);
|
||||
if ($existing) {
|
||||
$fixedUpdates[$column] = (int) $existing['lims_value_id'];
|
||||
$key = 'fx:' . $fixedKey . '|' . $raw;
|
||||
if (!isset($savedBindings[$key])) {
|
||||
$savedBindings[$key] = [
|
||||
'kind' => 'fixed',
|
||||
'fixed_field_key' => $fixedKey,
|
||||
'field_label' => binding_fixed_label($fixedKey),
|
||||
'json_value' => $raw,
|
||||
'lims_value' => (string) $existing['lims_value'],
|
||||
'lims_value_id' => (int) $existing['lims_value_id'],
|
||||
'datadb_ids' => [],
|
||||
];
|
||||
}
|
||||
$savedBindings[$key]['datadb_ids'][] = (int) $iddatadb;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Auto-match 1-a-1 (solo per le liste globali piccole).
|
||||
$autoMatch = null;
|
||||
if (binding_fixed_auto_matchable($fixedKey)) {
|
||||
$fixedValues = binding_get_fixed_values($pdo, $fixedKey, (int) $template_id);
|
||||
$autoMatch = binding_auto_match_fixed($fixedValues, $raw);
|
||||
}
|
||||
|
||||
if ($autoMatch) {
|
||||
binding_upsert_fixed($pdo, (int) $template_id, $fixedKey, $raw, (int) $autoMatch['id'], (string) $autoMatch['text'], $user_id);
|
||||
$fixedUpdates[$column] = (int) $autoMatch['id'];
|
||||
$key = 'fx:' . $fixedKey . '|' . $raw;
|
||||
if (!isset($autoBindings[$key])) {
|
||||
$autoBindings[$key] = [
|
||||
'kind' => 'fixed',
|
||||
'fixed_field_key' => $fixedKey,
|
||||
'field_label' => binding_fixed_label($fixedKey),
|
||||
'json_value' => $raw,
|
||||
'lims_value' => (string) $autoMatch['text'],
|
||||
'lims_value_id' => (int) $autoMatch['id'],
|
||||
'datadb_ids' => [],
|
||||
];
|
||||
}
|
||||
$autoBindings[$key]['datadb_ids'][] = (int) $iddatadb;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Nessuna corrispondenza: colonna lasciata vuota, segnalo come pending.
|
||||
$key = 'fx:' . $fixedKey . '|' . $raw;
|
||||
if (!isset($pendingBindings[$key])) {
|
||||
$pendingBindings[$key] = [
|
||||
'kind' => 'fixed',
|
||||
'fixed_field_key' => $fixedKey,
|
||||
'field_label' => binding_fixed_label($fixedKey),
|
||||
'json_value' => $raw,
|
||||
'datadb_ids' => [],
|
||||
];
|
||||
}
|
||||
$pendingBindings[$key]['datadb_ids'][] = (int) $iddatadb;
|
||||
}
|
||||
|
||||
if (!empty($fixedUpdates)) {
|
||||
$setParts = [];
|
||||
$params = [];
|
||||
foreach ($fixedUpdates as $col => $val) {
|
||||
$setParts[] = "`$col` = ?";
|
||||
$params[] = $val;
|
||||
}
|
||||
$params[] = (int) $iddatadb;
|
||||
$upd = $pdo->prepare("UPDATE datadb SET " . implode(', ', $setParts) . " WHERE iddatadb = ?");
|
||||
$upd->execute($params);
|
||||
$pdo->commit();
|
||||
} else {
|
||||
error_log("[AUTO FIELD 244] Nessun mapping field_id=244 per template $template_id");
|
||||
}
|
||||
} else {
|
||||
error_log("[AUTO FIELD 244] limsUserId vuoto per user_id=$user_id o nessuna riga inserita");
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// Non bloccare l'import se questo fallisce; logga soltanto
|
||||
error_log("[AUTO FIELD 244] " . $e->getMessage());
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
}
|
||||
|
||||
$_SESSION['inserted_ids'] = $insertedIds;
|
||||
|
||||
$importedUrl = "imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode);
|
||||
|
||||
// Solo se restano binding da risolvere mostro la pagina (con anche gli auto, modificabili).
|
||||
if (!empty($pendingBindings)) {
|
||||
$_SESSION['pending_bindings'] = [
|
||||
'template_id' => $template_id,
|
||||
'importref' => $importReferenceCode,
|
||||
'items' => array_values($pendingBindings),
|
||||
'auto' => array_values($autoBindings),
|
||||
'saved' => array_values($savedBindings),
|
||||
];
|
||||
|
||||
header("Location: resolve_bindings.php");
|
||||
exit;
|
||||
}
|
||||
|
||||
unset($_SESSION['pending_bindings']);
|
||||
|
||||
// Solo auto-collegati: vado diretto alla griglia, segnalando quanti.
|
||||
if (!empty($autoBindings)) {
|
||||
$importedUrl .= "&autobound=" . count($autoBindings);
|
||||
}
|
||||
|
||||
header("Location: " . $importedUrl);
|
||||
header("Location: imported.php?id=" . urlencode($template_id) . "&importref=" . urlencode($importReferenceCode));
|
||||
exit;
|
||||
|
||||
@@ -1,829 +0,0 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
|
||||
// Check if a valid template ID has been provided
|
||||
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
|
||||
header("Location: xlstemplates_grid.php?status=error&message=" . urlencode("Invalid ID"));
|
||||
exit;
|
||||
}
|
||||
|
||||
$id = intval($_GET['id']);
|
||||
|
||||
// Load template
|
||||
$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;
|
||||
}
|
||||
|
||||
// Check mappings
|
||||
$stmt = $pdo->prepare("SELECT id FROM template_mapping WHERE template_id = ?");
|
||||
$stmt->execute([$id]);
|
||||
$hasMappings = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
error_log("Loaded JSON import 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: 120px;
|
||||
max-width: 260px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.json-code-input {
|
||||
font-size: 1.15rem;
|
||||
min-height: 48px;
|
||||
}
|
||||
|
||||
.json-paste-area {
|
||||
min-height: 260px;
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.json-help-box {
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
padding: 12px 14px;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.source-badge {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
<title><?= htmlspecialchars($template['name']) ?> - JSON Import - <?= 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 me-2">To LIMS (l)</a>
|
||||
<a href="bindings_manage.php?template_id=<?= $id ?>" class="btn btn-outline-secondary">Binding JSON → LIMS</a>
|
||||
</div>
|
||||
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div>
|
||||
<h6 class="mb-0"><?= htmlspecialchars($template['name']) ?> - JSON Import</h6>
|
||||
<small>
|
||||
Template ID: <?= $id ?>
|
||||
</small>
|
||||
</div>
|
||||
<span class="badge bg-info text-dark">JSON mode</span>
|
||||
</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; ?>
|
||||
|
||||
<div class="json-help-box mb-3">
|
||||
<strong>Flusso:</strong> inserisci/scansiona un codice per recuperare il JSON da API, oppure incolla manualmente un JSON nel secondo tab.
|
||||
Ogni JSON aggiunto diventa una riga della tabella di preview. Quando hai finito, seleziona le righe e clicca <strong>Prosegui</strong>.
|
||||
</div>
|
||||
|
||||
<ul class="nav nav-tabs" id="jsonImportTabs" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link active" id="api-code-tab" data-bs-toggle="tab" data-bs-target="#api-code-pane" type="button" role="tab" aria-controls="api-code-pane" aria-selected="true">
|
||||
Code / Barcode
|
||||
</button>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<button class="nav-link" id="paste-json-tab" data-bs-toggle="tab" data-bs-target="#paste-json-pane" type="button" role="tab" aria-controls="paste-json-pane" aria-selected="false">
|
||||
Paste JSON
|
||||
</button>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content border border-top-0 p-3 mb-3" id="jsonImportTabsContent">
|
||||
<div class="tab-pane fade show active" id="api-code-pane" role="tabpanel" aria-labelledby="api-code-tab" tabindex="0">
|
||||
<form id="jsonCodeForm" class="row g-3 align-items-end">
|
||||
<div class="col-lg-8">
|
||||
<label for="json_code" class="form-label">Code / Barcode</label>
|
||||
<input type="text" class="form-control json-code-input" id="json_code" name="json_code" placeholder="Write or scan code" autocomplete="off" <?= !$hasMappings ? 'disabled' : '' ?>>
|
||||
<small class="text-muted">Lo scanner barcode normalmente scrive qui il codice e invia Enter.</small>
|
||||
</div>
|
||||
<div class="col-lg-4 d-flex gap-2">
|
||||
<button type="submit" class="btn btn-primary flex-fill" <?= !$hasMappings ? 'disabled' : '' ?>>Load JSON</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="clearCodeBtn">Clear</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane fade" id="paste-json-pane" role="tabpanel" aria-labelledby="paste-json-tab" tabindex="0">
|
||||
<form id="pasteJsonForm">
|
||||
<div class="mb-3">
|
||||
<label for="manual_json_reference" class="form-label">Reference / filename</label>
|
||||
<input type="text" class="form-control" id="manual_json_reference" placeholder="Optional reference, e.g. manual-json-001">
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="manual_json" class="form-label">Paste JSON</label>
|
||||
<textarea class="form-control json-paste-area" id="manual_json" placeholder='{"data":[{"id":"MM000620","attributes":{"trf_type":"apparel"}}]}' <?= !$hasMappings ? 'disabled' : '' ?>></textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary" <?= !$hasMappings ? 'disabled' : '' ?>>Add pasted JSON</button>
|
||||
<button type="button" class="btn btn-outline-secondary" id="clearManualJsonBtn">Clear</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="loader" id="loader"></div>
|
||||
<div id="errorContainer" class="alert alert-danger mt-3" style="display:none;"></div>
|
||||
<div id="successContainer" class="alert alert-success mt-3" style="display:none;"></div>
|
||||
|
||||
<div id="tableContainer"></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 TEMPLATE_ID = <?= (int)$id ?>;
|
||||
const API_ENDPOINT = 'get_json_by_code.php';
|
||||
const INCLUDE_SOURCE_CODE_COLUMN = true;
|
||||
const UNWRAP_SINGLE_DATA_ITEM = true;
|
||||
|
||||
const jsonCodeForm = document.getElementById('jsonCodeForm');
|
||||
const pasteJsonForm = document.getElementById('pasteJsonForm');
|
||||
const jsonCodeInput = document.getElementById('json_code');
|
||||
const manualJsonInput = document.getElementById('manual_json');
|
||||
const manualJsonReferenceInput = document.getElementById('manual_json_reference');
|
||||
const clearCodeBtn = document.getElementById('clearCodeBtn');
|
||||
const clearManualJsonBtn = document.getElementById('clearManualJsonBtn');
|
||||
const loader = document.getElementById('loader');
|
||||
const errorContainer = document.getElementById('errorContainer');
|
||||
const successContainer = document.getElementById('successContainer');
|
||||
const tableContainer = document.getElementById('tableContainer');
|
||||
|
||||
let jsonRows = [];
|
||||
let columns = [];
|
||||
|
||||
if (jsonCodeInput && !jsonCodeInput.disabled) {
|
||||
jsonCodeInput.focus();
|
||||
}
|
||||
|
||||
jsonCodeForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
const code = jsonCodeInput.value.trim();
|
||||
|
||||
if (!code) {
|
||||
showError('Inserisci o scansiona un codice.');
|
||||
return;
|
||||
}
|
||||
|
||||
loadJsonFromApi(code);
|
||||
});
|
||||
|
||||
pasteJsonForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const rawJson = manualJsonInput.value.trim();
|
||||
const reference = manualJsonReferenceInput.value.trim() || 'manual-json-' + (jsonRows.length + 1);
|
||||
|
||||
if (!rawJson) {
|
||||
showError('Incolla un JSON prima di aggiungerlo.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedJson = JSON.parse(rawJson);
|
||||
addJsonRow(parsedJson, reference, 'paste');
|
||||
manualJsonInput.value = '';
|
||||
manualJsonReferenceInput.value = '';
|
||||
showSuccess('JSON incollato aggiunto correttamente.');
|
||||
} catch (err) {
|
||||
showError('JSON non valido: ' + err.message);
|
||||
}
|
||||
});
|
||||
|
||||
clearCodeBtn.addEventListener('click', function() {
|
||||
jsonCodeInput.value = '';
|
||||
jsonCodeInput.focus();
|
||||
});
|
||||
|
||||
clearManualJsonBtn.addEventListener('click', function() {
|
||||
manualJsonInput.value = '';
|
||||
manualJsonReferenceInput.value = '';
|
||||
});
|
||||
|
||||
function loadJsonFromApi(code) {
|
||||
hideMessages();
|
||||
loader.style.display = 'block';
|
||||
|
||||
fetch(API_ENDPOINT, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
template_id: TEMPLATE_ID,
|
||||
code: code
|
||||
})
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
throw new Error('HTTP status ' + response.status);
|
||||
}
|
||||
return response.json();
|
||||
})
|
||||
.then(responseData => {
|
||||
loader.style.display = 'none';
|
||||
|
||||
if (responseData.error) {
|
||||
showError(responseData.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let jsonPayload = responseData;
|
||||
let reference = code;
|
||||
|
||||
// Supported endpoint response format:
|
||||
// { success: true, json: {...}, reference: "..." }
|
||||
if (responseData.json !== undefined) {
|
||||
jsonPayload = responseData.json;
|
||||
reference = responseData.reference || responseData.filename || responseData.code || code;
|
||||
}
|
||||
|
||||
addJsonRow(jsonPayload, reference, 'api');
|
||||
jsonCodeInput.value = '';
|
||||
jsonCodeInput.focus();
|
||||
showSuccess('JSON recuperato e aggiunto correttamente.');
|
||||
})
|
||||
.catch(error => {
|
||||
loader.style.display = 'none';
|
||||
showError('Errore durante il recupero del JSON: ' + error.message);
|
||||
});
|
||||
}
|
||||
|
||||
function addJsonRow(jsonPayload, reference, sourceType) {
|
||||
// Ogni elemento di data[] diventa una riga (non colonne data.0.*, data.1.*).
|
||||
const records = extractRecords(jsonPayload);
|
||||
|
||||
records.forEach((record, recordIndex) => {
|
||||
const flattened = flattenJson(record);
|
||||
|
||||
const rowReference = records.length > 1 ?
|
||||
reference + '#' + (recordIndex + 1) :
|
||||
reference;
|
||||
|
||||
if (INCLUDE_SOURCE_CODE_COLUMN) {
|
||||
flattened.source_code = rowReference;
|
||||
flattened.source_type = sourceType;
|
||||
}
|
||||
|
||||
const newColumns = Object.keys(flattened).filter(col => !columns.includes(col));
|
||||
columns = columns.concat(newColumns);
|
||||
|
||||
jsonRows.push({
|
||||
excelrow: 'JSON-' + (jsonRows.length + 1),
|
||||
reference: rowReference,
|
||||
sourceType: sourceType,
|
||||
flat: flattened
|
||||
});
|
||||
});
|
||||
|
||||
renderTable();
|
||||
}
|
||||
|
||||
// Record da trasformare in righe: gli oggetti in data[], altrimenti il payload stesso.
|
||||
function extractRecords(payload) {
|
||||
if (
|
||||
payload &&
|
||||
typeof payload === 'object' &&
|
||||
!Array.isArray(payload) &&
|
||||
Array.isArray(payload.data) &&
|
||||
payload.data.length > 0
|
||||
) {
|
||||
const items = payload.data.filter(item => item && typeof item === 'object');
|
||||
if (items.length > 0) {
|
||||
return items;
|
||||
}
|
||||
}
|
||||
|
||||
return [payload];
|
||||
}
|
||||
|
||||
function flattenJson(value, prefix = '', result = {}) {
|
||||
if (value === null || value === undefined) {
|
||||
result[prefix || 'value'] = '';
|
||||
return result;
|
||||
}
|
||||
|
||||
if (Array.isArray(value)) {
|
||||
if (value.length === 0) {
|
||||
result[prefix || 'value'] = '';
|
||||
return result;
|
||||
}
|
||||
|
||||
value.forEach((item, index) => {
|
||||
const newPrefix = prefix ? prefix + '.' + index : String(index);
|
||||
flattenJson(item, newPrefix, result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
if (typeof value === 'object') {
|
||||
const keys = Object.keys(value);
|
||||
if (keys.length === 0) {
|
||||
result[prefix || 'value'] = '';
|
||||
return result;
|
||||
}
|
||||
|
||||
keys.forEach(key => {
|
||||
const newPrefix = prefix ? prefix + '.' + key : key;
|
||||
flattenJson(value[key], newPrefix, result);
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
result[prefix || 'value'] = String(value);
|
||||
return result;
|
||||
}
|
||||
|
||||
function buildImportData() {
|
||||
const rows = jsonRows.map(row => columns.map(col => row.flat[col] ?? ''));
|
||||
const excelData = rows.map((rowData, index) => ({
|
||||
excelrow: jsonRows[index].excelrow,
|
||||
data: rowData
|
||||
}));
|
||||
|
||||
return {
|
||||
template_id: TEMPLATE_ID,
|
||||
columns: columns,
|
||||
rows: rows,
|
||||
excel_data: excelData,
|
||||
filename: 'json_import_' + new Date().toISOString().replace(/[:.]/g, '-').slice(0, 19)
|
||||
};
|
||||
}
|
||||
|
||||
function renderTable() {
|
||||
if (jsonRows.length === 0) {
|
||||
tableContainer.innerHTML = '';
|
||||
return;
|
||||
}
|
||||
|
||||
const data = buildImportData();
|
||||
|
||||
let html = `
|
||||
<form id="selectRowsForm" action="import_insert.php" method="POST">
|
||||
<input type="hidden" name="template_id" value="${escapeHtml(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="${escapeHtml(data.filename)}">
|
||||
<input type="hidden" name="source_type" value="json">
|
||||
|
||||
<div class="d-flex justify-content-between align-items-center flex-wrap gap-2 mb-3">
|
||||
<div>
|
||||
<strong>JSON rows loaded:</strong> ${jsonRows.length}
|
||||
<span class="badge bg-secondary source-badge ms-2">Columns: ${data.columns.length}</span>
|
||||
</div>
|
||||
<div class="d-flex gap-2">
|
||||
<button type="button" class="btn btn-outline-danger btn-sm" id="clearAllRowsBtn">Clear all rows</button>
|
||||
<button type="submit" class="btn btn-primary" id="proceedButtonTop" disabled>Prosegui</button>
|
||||
</div>
|
||||
</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"> Select</th>
|
||||
${data.columns.map(col => `<th title="${escapeHtml(col)}">${escapeHtml(readableColumnLabel(col))}<div class="resize-handle"></div></th>`).join('')}
|
||||
<th>Action</th>
|
||||
</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('')}
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${data.excel_data.map((row, index) => `
|
||||
<tr data-row-index="${index}">
|
||||
<td><input type="checkbox" class="row-checkbox" name="selected_rows[]" value="${index}" data-excelrow="${escapeHtml(row.excelrow)}"></td>
|
||||
${row.data.map(cell => `<td title="${escapeHtml(cell)}">${escapeHtml(cell)}</td>`).join('')}
|
||||
<td><button type="button" class="btn btn-sm btn-outline-danger remove-row-btn" data-row-index="${index}">Remove</button></td>
|
||||
</tr>
|
||||
`).join('')}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary mt-3" id="proceedButtonBottom" disabled>Prosegui</button>
|
||||
</form>
|
||||
`;
|
||||
|
||||
tableContainer.innerHTML = html;
|
||||
bindTableEvents(data);
|
||||
}
|
||||
|
||||
function bindTableEvents(data) {
|
||||
const selectRowsForm = document.getElementById('selectRowsForm');
|
||||
const clearAllRowsBtn = document.getElementById('clearAllRowsBtn');
|
||||
const removeRowButtons = document.querySelectorAll('.remove-row-btn');
|
||||
|
||||
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));
|
||||
});
|
||||
|
||||
clearAllRowsBtn.addEventListener('click', function() {
|
||||
if (!confirm('Vuoi rimuovere tutte le righe JSON caricate?')) return;
|
||||
jsonRows = [];
|
||||
columns = [];
|
||||
renderTable();
|
||||
showSuccess('Righe JSON rimosse.');
|
||||
jsonCodeInput.focus();
|
||||
});
|
||||
|
||||
removeRowButtons.forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const rowIndex = parseInt(this.dataset.rowIndex, 10);
|
||||
jsonRows.splice(rowIndex, 1);
|
||||
rebuildColumnsFromRows();
|
||||
renderTable();
|
||||
});
|
||||
});
|
||||
|
||||
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(document.querySelectorAll('.row-checkbox')).some(cb => cb.checked);
|
||||
|
||||
if (proceedButtonTop) proceedButtonTop.disabled = !enabled;
|
||||
if (proceedButtonBottom) proceedButtonBottom.disabled = !enabled;
|
||||
}
|
||||
|
||||
selectAllCheckbox.addEventListener('change', function() {
|
||||
const visibleRows = Array.from(document.querySelectorAll('#importPreviewTable 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() {
|
||||
const visibleCheckboxes = Array.from(document.querySelectorAll('#importPreviewTable 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('#importPreviewTable 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(60, startWidth + (e.clientX - startX));
|
||||
th.style.width = `${newWidth}px`;
|
||||
th.style.minWidth = `${newWidth}px`;
|
||||
th.style.maxWidth = `${newWidth}px`;
|
||||
|
||||
const cells = document.querySelectorAll(`#importPreviewTable 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('#importPreviewTable tbody tr');
|
||||
const filterInputs = document.querySelectorAll('.column-filter');
|
||||
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('#importPreviewTable 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;
|
||||
activeFilters[colIndex] = this.value;
|
||||
applyColumnFilters();
|
||||
});
|
||||
});
|
||||
|
||||
updateProceedButton();
|
||||
}
|
||||
|
||||
function rebuildColumnsFromRows() {
|
||||
columns = [];
|
||||
jsonRows.forEach(row => {
|
||||
Object.keys(row.flat).forEach(col => {
|
||||
if (!columns.includes(col)) {
|
||||
columns.push(col);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function readableColumnLabel(columnName) {
|
||||
if (!columnName) return 'Column without name';
|
||||
return columnName;
|
||||
}
|
||||
|
||||
function showError(message) {
|
||||
successContainer.style.display = 'none';
|
||||
errorContainer.textContent = message;
|
||||
errorContainer.style.display = 'block';
|
||||
}
|
||||
|
||||
function showSuccess(message) {
|
||||
errorContainer.style.display = 'none';
|
||||
successContainer.textContent = message;
|
||||
successContainer.style.display = 'block';
|
||||
setTimeout(() => {
|
||||
successContainer.style.display = 'none';
|
||||
}, 3500);
|
||||
}
|
||||
|
||||
function hideMessages() {
|
||||
errorContainer.style.display = 'none';
|
||||
successContainer.style.display = 'none';
|
||||
}
|
||||
|
||||
function escapeHtml(value) {
|
||||
return String(value ?? '')
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -147,6 +147,7 @@ $desiredOrder = [
|
||||
'ClienteResponsabile',
|
||||
'ClienteFornitore',
|
||||
'ClienteAnalisi',
|
||||
'ClienteFatturazione',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService',
|
||||
'MoltiplicatorePrezzo',
|
||||
@@ -181,6 +182,7 @@ $fixedAliasMap = [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'ClienteFatturazione' => 'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
@@ -285,6 +287,21 @@ $gridColumns[] = ['type' => 'idclient', 'key' => 'idclient', 'label' => 'Client'
|
||||
// 4. Cliente Fornitore
|
||||
$gridColumns[] = ['type' => 'cliente_fornitore_id', 'key' => 'cliente_fornitore_id', 'label' => $slugMapping['ClienteFornitore'] ?? 'ClienteFornitore', 'width' => 300];
|
||||
|
||||
// 4b. Fatturazione Avanzata — subito dopo Cliente Fornitore
|
||||
foreach ($fixedFields as $f) {
|
||||
if ($f['fixed_field_key'] === 'ClienteFatturazione') {
|
||||
$gridColumns[] = [
|
||||
'type' => 'fixed',
|
||||
'key' => 'ClienteFatturazione',
|
||||
'label' => 'Fatturazione avanzata',
|
||||
'dataType' => $f['data_type'],
|
||||
'isRequired' => (bool)$f['is_required'],
|
||||
'width' => 180,
|
||||
];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Other custom fields in schema order
|
||||
foreach ($allMappings as $mapping) {
|
||||
if (
|
||||
@@ -321,7 +338,18 @@ $gridColumns[] = ['type' => 'tracking', 'key' => 'tracking', 'label' => 'Trackin
|
||||
// 10. Fixed fields (with importreferencecode/filename/importdate after ConsegnaRichiesta)
|
||||
foreach ($fixedFields as $f) {
|
||||
$key = $f['fixed_field_key'];
|
||||
$label = $slugMapping[$key] ?? $key;
|
||||
|
||||
if ($key === 'ClienteFatturazione') continue; // già renderizzata al punto 4b
|
||||
|
||||
// Cambia solo il titolo della colonna ClienteAnalisi
|
||||
if ($key === 'ClienteAnalisi') {
|
||||
$label = 'Buyer';
|
||||
} elseif ($key === 'ClienteFatturazione') {
|
||||
$label = 'Fatturazione avanzata';
|
||||
} else {
|
||||
$label = $slugMapping[$key] ?? $key;
|
||||
}
|
||||
|
||||
$gridColumns[] = [
|
||||
'type' => 'fixed',
|
||||
'key' => $key,
|
||||
@@ -330,6 +358,7 @@ foreach ($fixedFields as $f) {
|
||||
'isRequired' => (bool)$f['is_required'],
|
||||
'width' => 180,
|
||||
];
|
||||
|
||||
if ($key === 'ConsegnaRichiesta') {
|
||||
$gridColumns[] = ['type' => 'static', 'key' => 'importreferencecode', 'label' => $slugMapping['importreferencecode'] ?? 'Import Reference Code', 'width' => 150, 'editable' => false];
|
||||
$gridColumns[] = ['type' => 'static', 'key' => 'filename_import', 'label' => $slugMapping['filename_import'] ?? 'File', 'width' => 150, 'editable' => false];
|
||||
@@ -468,8 +497,16 @@ $gridMeta = [
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 0.25rem;
|
||||
background: #f8f9fa;
|
||||
position: sticky;
|
||||
top: 70px;
|
||||
/* regola sull'altezza reale della tua topbar fissa */
|
||||
position: sticky;
|
||||
top: 70px;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.top-scrollbar-inner {
|
||||
height: 1px;
|
||||
}
|
||||
@@ -1301,17 +1338,9 @@ $gridMeta = [
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
<?php $autoBoundCount = isset($_GET['autobound']) ? (int) $_GET['autobound'] : 0; ?>
|
||||
<?php if ($autoBoundCount > 0): ?>
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
<?= $autoBoundCount ?> valore/i collegato/i automaticamente al LIMS durante l'import.
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="mb-3 text d-flex align-items-center gap-2">
|
||||
<a href="imported.php?id=<?= $template_id ?>" class="btn btn-warning me-2">Imported (i)</a>
|
||||
<a href="tolims.php?id=<?= $template_id ?>" class="btn btn-success">To LIMS (l)</a>
|
||||
<a href="bindings_manage.php?template_id=<?= $template_id ?>" class="btn btn-outline-secondary">Binding JSON → LIMS</a>
|
||||
<?php if ($importref === ''): ?>
|
||||
<span class="ms-3">
|
||||
<label class="form-check-label" style="font-size: 13px; cursor: pointer;">
|
||||
@@ -1375,17 +1404,8 @@ $gridMeta = [
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center" style="min-height: 42px; gap: 12px;">
|
||||
<div class="dropdown actions-dropdown" style="flex-shrink: 0;">
|
||||
<button class="dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
|
||||
<i class="fas fa-cogs"></i> Actions
|
||||
</button>
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
|
||||
<li><a class="dropdown-item export-all-lims-btn" href="#"><i class="fas fa-upload" style="color: #eb0b0b;"></i>Export All</a></li>
|
||||
|
||||
<li><a class="dropdown-item save-all-btn" href="#"><i class="fas fa-save" style="color: #28a745;"></i>Save All</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a href="#" class="btn btn-outline-danger btn-sm export-all-lims-btn" style="flex-shrink:0;"><i class="fas fa-upload"></i> Export All</a>
|
||||
<a href="#" class="btn btn-outline-success btn-sm save-all-btn" style="flex-shrink:0;"><i class="fas fa-save"></i> Save All</a>
|
||||
<button type="button" class="btn btn-outline-success btn-sm" id="addRowBtn" style="flex-shrink:0;"><i class="fas fa-plus"></i> Add Row</button>
|
||||
<div id="unsavedChanges" style="display:none; color: red; font-weight: bold; font-size: 13px;">
|
||||
⚠️ Unsaved changes detected!
|
||||
|
||||
@@ -5,7 +5,7 @@ $db = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL | E_STRICT);
|
||||
error_reporting(E_ALL);
|
||||
// This should be equal to: PATH_TO_VANGUARD_FOLDER/extra/auth.php
|
||||
include('../../extra/auth.php');
|
||||
//require_once __DIR__ . '/extra/auth.php';
|
||||
|
||||
@@ -6,110 +6,155 @@
|
||||
<div>
|
||||
<h4 class="logo-text"><?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?></h4>
|
||||
</div>
|
||||
<div class="toggle-icon ms-auto"><i class='bx bx-arrow-back'></i>
|
||||
<div class="toggle-icon ms-auto">
|
||||
<i class='bx bx-arrow-back'></i>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--navigation-->
|
||||
<ul class="metismenu" id="menu">
|
||||
<!-- user, admin, superuser menù -->
|
||||
<?php if ((Auth::user()->hasRole('Admin')) || (Auth::user()->hasRole('User')) || (Auth::user()->hasRole('Superuser'))) : ?>
|
||||
|
||||
<?php
|
||||
$currentUser = Auth::user();
|
||||
|
||||
$isAdmin = $currentUser->hasRole('Admin');
|
||||
$isSuperUser = $currentUser->hasRole('SuperUser');
|
||||
$isUser = $currentUser->hasRole('User');
|
||||
|
||||
$canAccessMainMenu = $isAdmin || $isSuperUser || $isUser;
|
||||
$canAccessUserAdmin = $isAdmin || $isSuperUser;
|
||||
$canAccessTemplates = $isAdmin || $isSuperUser;
|
||||
?>
|
||||
|
||||
<!-- user, admin, superuser menu -->
|
||||
<?php if ($canAccessMainMenu) : ?>
|
||||
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon"><i class='bx bx-home-alt'></i>
|
||||
<div class="parent-icon">
|
||||
<i class='bx bx-home-alt'></i>
|
||||
</div>
|
||||
<div class="menu-title">Dashboard</div>
|
||||
</a>
|
||||
<ul>
|
||||
<!-- <li> <a href="index.php"><i class='bx bx-radio-circle'></i>Default</a>
|
||||
</li> -->
|
||||
<li> <a href="import_dashboard.php"><i class='bx bx-radio-circle'></i>Import AREA</a>
|
||||
<li>
|
||||
<a href="import_dashboard.php">
|
||||
<i class='bx bx-radio-circle'></i>XLS Import
|
||||
</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon"><i class="bx bx-category"></i>
|
||||
</div>
|
||||
<div class="menu-title">Templates</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li> <a href="templates_dashboard.php"><i class='bx bx-radio-circle'></i><?= htmlspecialchars($dashtemplate, ENT_QUOTES, 'UTF-8'); ?></a>
|
||||
</li>
|
||||
<li> <a href="insert_template_xls.php"><i class='bx bx-radio-circle'></i><?= htmlspecialchars($insertnewtemplatexls, ENT_QUOTES, 'UTF-8'); ?></a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
<?php if ($canAccessTemplates) : ?>
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-category"></i>
|
||||
</div>
|
||||
<div class="menu-title">Templates</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="templates_dashboard.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($dashtemplate, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="insert_template_xls.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($insertnewtemplatexls, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<li>
|
||||
<a href="javascript:;" class="has-arrow">
|
||||
<div class="parent-icon"><i class="bx bx-category"></i>
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-category"></i>
|
||||
</div>
|
||||
<div class="menu-title">Other Functions</div>
|
||||
</a>
|
||||
<ul>
|
||||
<li> <a href="quotations.php"><i class='bx bx-radio-circle'></i><?php echo $quotationstitle; ?></a>
|
||||
<li>
|
||||
<a href="quotations.php">
|
||||
<i class='bx bx-radio-circle'></i><?= htmlspecialchars($quotationstitle, ENT_QUOTES, 'UTF-8'); ?>
|
||||
</a>
|
||||
</li>
|
||||
<li> <a href="bindings_manage.php"><i class='bx bx-radio-circle'></i>Binding JSON → LIMS</a>
|
||||
</li>
|
||||
|
||||
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<?php if ($canAccessUserAdmin) : ?>
|
||||
<li class="menu-label">Administration</li>
|
||||
|
||||
<li class="menu-label">Reports</li>
|
||||
<li>
|
||||
<a href="rapporti_cliente_lookup.php" target="">
|
||||
<div class="parent-icon"><i class="bx bx-file-find"></i>
|
||||
</div>
|
||||
<div class="menu-title">Ricerca Reports</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="user-admin.php">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-user-plus"></i>
|
||||
</div>
|
||||
<div class="menu-title">User Admin</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="stats_export_lims.php">
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-user-plus"></i>
|
||||
</div>
|
||||
<div class="menu-title">Stats</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
|
||||
<li class="menu-label">Others</li>
|
||||
|
||||
|
||||
<li>
|
||||
<a href="https://helpdesk.cesoft.io" target="_blank">
|
||||
<div class="parent-icon"><i class="bx bx-support"></i>
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-support"></i>
|
||||
</div>
|
||||
<div class="menu-title">Support</div>
|
||||
</a>
|
||||
</li>
|
||||
<?php
|
||||
endif; ?>
|
||||
<!-- admin, superuser menù -->
|
||||
<?php if ((Auth::user()->hasRole('Admin')) || (Auth::user()->hasRole('Superuser'))) : ?>
|
||||
<?php
|
||||
endif; ?>
|
||||
<!-- admin menù -->
|
||||
<?php if (Auth::user()->hasRole('Admin')) : ?>
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
|
||||
<!-- admin only menu -->
|
||||
<?php if ($isAdmin) : ?>
|
||||
|
||||
<li class="menu-label">Admin Menù</li>
|
||||
|
||||
<li>
|
||||
<a href="../" target="_blank">
|
||||
<div class="parent-icon"><i class="bx bx-support"></i>
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-support"></i>
|
||||
</div>
|
||||
<div class="menu-title">User Management</div>
|
||||
</a>
|
||||
</li>
|
||||
<!-- <li>
|
||||
|
||||
<!--
|
||||
<li>
|
||||
<a href="template/index.html" target="_blank">
|
||||
<div class="parent-icon"><i class="bx bx-support"></i>
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-support"></i>
|
||||
</div>
|
||||
<div class="menu-title">Template</div>
|
||||
<div class="menu-title">Template</div>
|
||||
</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="https://codervent.com/rocker/documentation/index.html" target="_blank">
|
||||
<div class="parent-icon"><i class="bx bx-folder"></i>
|
||||
<div class="parent-icon">
|
||||
<i class="bx bx-folder"></i>
|
||||
</div>
|
||||
<div class="menu-title">Documentation</div>
|
||||
</a>
|
||||
</li> -->
|
||||
<?php
|
||||
endif; ?>
|
||||
</li>
|
||||
-->
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</ul>
|
||||
<!--end navigation-->
|
||||
</div>
|
||||
@@ -0,0 +1,55 @@
|
||||
<?php
|
||||
$popupEnabled = (($_ENV['POPUP_ENABLED'] ?? getenv('POPUP_ENABLED')) === 'true');
|
||||
|
||||
if ($popupEnabled):
|
||||
?>
|
||||
<div id="app-popup-overlay" style="position:fixed;inset:0;background:rgba(0,0,0,.65);display:flex;align-items:center;justify-content:center;z-index:99999;padding:20px;">
|
||||
<div style="background:#fff;max-width:640px;width:100%;max-height:90vh;overflow-y:auto;padding:40px;border-radius:14px;position:relative;box-shadow:0 20px 60px rgba(0,0,0,.4);text-align:center;">
|
||||
|
||||
<div style="font-size:46px;line-height:1;margin-bottom:10px;">🚀</div>
|
||||
|
||||
<h2 style="margin:0 0 8px;font-size:26px;color:#111;">Nuovo sito in produzione</h2>
|
||||
<p style="font-size:16px;color:#444;margin:0 0 20px;">Il portale si è spostato su un nuovo indirizzo.</p>
|
||||
|
||||
<div style="background:#f1f5ff;border:1px solid #c9d8ff;border-radius:10px;padding:16px;margin-bottom:24px;">
|
||||
<p style="margin:0 0 4px;font-size:13px;color:#667;text-transform:uppercase;letter-spacing:.5px;">Nuovo indirizzo</p>
|
||||
<p style="margin:0;font-size:19px;font-weight:700;color:#1d4ed8;word-break:break-all;">https://trfsmart-bv.cesoft.io/</p>
|
||||
</div>
|
||||
|
||||
<a href="https://trfsmart-bv.cesoft.io/"
|
||||
style="display:inline-block;padding:16px 36px;background:#2563eb;color:#fff;text-decoration:none;border-radius:8px;font-size:18px;font-weight:600;box-shadow:0 4px 14px rgba(37,99,235,.35);">
|
||||
Clicca qui per accedere al nuovo sito
|
||||
</a>
|
||||
|
||||
<p style="font-size:13px;color:#888;margin:16px 0 0;">Ti consigliamo di salvare il nuovo indirizzo nei preferiti.</p>
|
||||
|
||||
<hr style="border:none;border-top:1px solid #eee;margin:28px 0 20px;">
|
||||
|
||||
<div style="background:#fff8e6;border:1px solid #ffe08a;border-radius:10px;padding:16px 18px;text-align:left;margin-bottom:20px;">
|
||||
<p style="margin:0;font-size:14px;color:#8a6d00;">
|
||||
<strong>⚠️ Attenzione:</strong> chiudi questo messaggio <u>solo</u> se riscontri problemi
|
||||
con il nuovo sito. Continuando su questo portale userai la versione precedente.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<button onclick="chiudiPopupMigrazione()"
|
||||
style="padding:11px 22px;background:#f0f0f0;color:#555;border:1px solid #ddd;border-radius:8px;cursor:pointer;font-size:14px;">
|
||||
Ho problemi col nuovo sito, resto su questo portale
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
function chiudiPopupMigrazione() {
|
||||
var conferma = confirm(
|
||||
"Sei sicuro di voler restare sul portale precedente?\n\n" +
|
||||
"Il nuovo sito è quello ufficiale in produzione. " +
|
||||
"Chiudi questo avviso solo se hai riscontrato un problema reale sul nuovo sito."
|
||||
);
|
||||
if (conferma) {
|
||||
document.getElementById('app-popup-overlay').remove();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<?php endif; ?>
|
||||
@@ -1,8 +1,22 @@
|
||||
<header>
|
||||
<div class="topbar d-flex align-items-center">
|
||||
<nav class="navbar navbar-expand gap-3">
|
||||
<div class="mobile-toggle-menu"><i class='bx bx-menu'></i>
|
||||
|
||||
<?php
|
||||
// Controllo ambiente TEST
|
||||
$testEnv = strtolower($_ENV['TEST_ENVIRONMENT'] ?? getenv('TEST_ENVIRONMENT') ?? 'true');
|
||||
|
||||
if ($testEnv === 'true'):
|
||||
?>
|
||||
<div class="alert alert-danger border-0 rounded-0 mb-0 py-2 text-center fw-bold fs-5 w-100 shadow-sm"
|
||||
style="margin: 0 -15px 0 -15px;">
|
||||
<i class='bx bx-test-tube me-2'></i>
|
||||
AMBIENTE DI TEST — NON È PRODUZIONE
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<nav class="navbar navbar-expand gap-3 w-100">
|
||||
<!-- Tutto il resto del tuo codice della topbar rimane identico -->
|
||||
<div class="mobile-toggle-menu"><i class='bx bx-menu'></i></div>
|
||||
|
||||
<div class="search-bar d-lg-block d-none" data-bs-toggle="modal" data-bs-target="#SearchModal">
|
||||
<!-- <a href="avascript:;" class="btn d-flex align-items-center"><i class='bx bx-search'></i>Search</a> -->
|
||||
@@ -94,8 +108,14 @@
|
||||
<ul class="dropdown-menu dropdown-menu-end">
|
||||
<li><a class="dropdown-item d-flex align-items-center" href="user-profile.php"><i class="bx bx-user fs-5"></i><span>Profile</span></a>
|
||||
</li>
|
||||
<li><a class="dropdown-item d-flex align-items-center" href="settings.php"><i class="bx bx-cog fs-5"></i><span>Settings</span></a>
|
||||
</li>
|
||||
<?php if ($user->hasRole('Admin') || $user->hasRole('SuperUser')): ?>
|
||||
<li>
|
||||
<a class="dropdown-item d-flex align-items-center" href="user-admin.php">
|
||||
<i class="bx bx-user-plus fs-5"></i>
|
||||
<span>User Admin</span>
|
||||
</a>
|
||||
</li>
|
||||
<?php endif; ?>
|
||||
<li>
|
||||
<div class="dropdown-divider mb-0"></div>
|
||||
</li>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
require_once 'class/db-functions.php';
|
||||
|
||||
$response = ["success" => false, "data" => [], "message" => ""];
|
||||
@@ -12,11 +13,25 @@ try {
|
||||
throw new Exception('Database connection failed.');
|
||||
}
|
||||
|
||||
// Recupera solo i template attivi
|
||||
$stmt = $pdo->query("SELECT id, button_label, button_size, button_bg_color, button_text_color, source_type
|
||||
FROM excel_templates
|
||||
WHERE status = 'active'
|
||||
ORDER BY button_label ASC");
|
||||
$userId = $iduserlogin ?? 0;
|
||||
|
||||
// Recupera i template attivi, con flag is_favorite per l'utente corrente
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
et.id,
|
||||
et.button_label,
|
||||
et.button_size,
|
||||
et.button_bg_color,
|
||||
et.button_text_color,
|
||||
et.source_type,
|
||||
CASE WHEN uf.id IS NOT NULL THEN 1 ELSE 0 END AS is_favorite
|
||||
FROM excel_templates et
|
||||
LEFT JOIN user_template_favorites uf
|
||||
ON uf.template_id = et.id AND uf.user_id = :user_id
|
||||
WHERE et.status = 'active'
|
||||
ORDER BY et.button_label ASC
|
||||
");
|
||||
$stmt->execute(['user_id' => $userId]);
|
||||
$templates = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
$response["success"] = true;
|
||||
|
||||
@@ -77,7 +77,8 @@ $stmt = $pdo->prepare("
|
||||
field_label,
|
||||
main_field,
|
||||
is_visible_import,
|
||||
is_visible_parts
|
||||
is_visible_parts,
|
||||
is_addnote
|
||||
FROM template_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY field_order ASC, id ASC
|
||||
@@ -87,19 +88,10 @@ $mappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Recupera i fixed fields dalla tabella template_fixed_mapping
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
id,
|
||||
fixed_field_key,
|
||||
is_manual,
|
||||
data_type,
|
||||
is_required,
|
||||
default_value,
|
||||
default_source,
|
||||
json_node,
|
||||
is_visible_import
|
||||
FROM template_fixed_mapping
|
||||
WHERE template_id = ?
|
||||
ORDER BY id ASC
|
||||
SELECT 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([$id]);
|
||||
$fixedMappings = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||
@@ -192,7 +184,9 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
#schemaFieldsTable th:nth-child(2),
|
||||
#schemaFieldsTable td:nth-child(2),
|
||||
#schemaFieldsTable th:nth-child(3),
|
||||
#schemaFieldsTable td:nth-child(3) {
|
||||
#schemaFieldsTable td:nth-child(3),
|
||||
#schemaFieldsTable th:nth-child(4),
|
||||
#schemaFieldsTable td:nth-child(4) {
|
||||
width: 48px;
|
||||
/* 🔥 più stretto */
|
||||
text-align: center;
|
||||
@@ -204,7 +198,8 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
/* Make checkbox itself not oversized */
|
||||
#schemaFieldsTable td:nth-child(1) input[type="checkbox"],
|
||||
#schemaFieldsTable td:nth-child(2) input[type="checkbox"],
|
||||
#schemaFieldsTable td:nth-child(3) input[type="checkbox"] {
|
||||
#schemaFieldsTable td:nth-child(3) input[type="checkbox"],
|
||||
#schemaFieldsTable td:nth-child(4) input[type="checkbox"] {
|
||||
transform: scale(0.95);
|
||||
margin: 0;
|
||||
}
|
||||
@@ -407,6 +402,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
<th style="width:45px; text-align:center;">Main</th>
|
||||
<th style="width:45px; text-align:center;">Import</th>
|
||||
<th style="width:45px; text-align:center;">Parts</th>
|
||||
<th style="width:45px; text-align:center;">Note</th>
|
||||
<th style="width:320px;">Title</th>
|
||||
<th style="width:70px; text-align:center;">Order</th>
|
||||
<th style="width:120px;">Type</th>
|
||||
@@ -437,7 +433,12 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
||||
<?php echo ((int)($mapping['is_visible_parts'] ?? 0) === 1) ? 'checked' : ''; ?>>
|
||||
</td>
|
||||
|
||||
<td class="text-center">
|
||||
<input type="checkbox"
|
||||
class="addnote-checkbox"
|
||||
data-mapping-id="<?php echo (int)$mapping['id']; ?>"
|
||||
<?php echo ((int)($mapping['is_addnote'] ?? 0) === 1) ? 'checked' : ''; ?>>
|
||||
</td>
|
||||
<td class="title-col">
|
||||
<?php echo htmlspecialchars($mapping['field_label'] ?? 'N/A'); ?>
|
||||
<?php if ((int)$mapping['is_required'] === 1): ?>
|
||||
@@ -451,23 +452,22 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
|
||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||
|
||||
<td><?php echo htmlspecialchars($mapping['data_type'] ?? 'N/A'); ?></td>
|
||||
|
||||
<td>
|
||||
<?php
|
||||
$isSceltaMultipla = ($mapping['data_type'] === 'SceltaMultipla');
|
||||
$isApiSceltaMultipla = ($sourceType === 'API' && $isSceltaMultipla);
|
||||
|
||||
$autoValue = $mapping['auto_value'] ?? 'none';
|
||||
$hasAuto = ($autoValue && $autoValue !== 'none');
|
||||
|
||||
if ($sourceType === 'API' && !empty($mapping['json_node'])) {
|
||||
$mappingValue = 'json';
|
||||
} elseif ($sourceType === 'XLS' && !$isSceltaMultipla && !empty($mapping['excel_column'])) {
|
||||
$mappingValue = 'xls';
|
||||
} elseif (!$isSceltaMultipla && $hasAuto) {
|
||||
if ($isSceltaMultipla) {
|
||||
$mappingValue = 'manual';
|
||||
} elseif ($hasAuto) {
|
||||
$mappingValue = 'auto';
|
||||
} elseif ((int)$mapping['is_manual'] === 1 || $isSceltaMultipla) {
|
||||
} elseif ($sourceType === 'XLS' && !empty($mapping['excel_column'])) {
|
||||
$mappingValue = 'xls';
|
||||
} elseif ($sourceType === 'API' && !empty($mapping['json_node'])) {
|
||||
$mappingValue = 'json';
|
||||
} elseif ((int)$mapping['is_manual'] === 1) {
|
||||
$mappingValue = 'manual';
|
||||
} else {
|
||||
$mappingValue = '';
|
||||
@@ -477,18 +477,17 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
<select class="form-select mapping-select"
|
||||
data-id="<?php echo (int)$mapping['id']; ?>"
|
||||
data-field-id="<?php echo (int)$mapping['field_id']; ?>"
|
||||
data-is-scelta-multipla="<?php echo $isSceltaMultipla ? '1' : '0'; ?>"
|
||||
<?php echo ($isSceltaMultipla && $sourceType !== 'API') ? 'disabled' : ''; ?>>
|
||||
|
||||
<option value="">Select Option</option>
|
||||
|
||||
<?php if ($sourceType === 'XLS' && !$isSceltaMultipla): ?>
|
||||
<option value="xls" <?php echo ($mappingValue === 'xls') ? 'selected' : ''; ?>>Map to XLS Column</option>
|
||||
<?php elseif ($sourceType === 'API'): ?>
|
||||
<option value="json" <?php echo ($mappingValue === 'json') ? 'selected' : ''; ?>>Map to JSON Node</option>
|
||||
<?php endif; ?>
|
||||
<?php echo $isSceltaMultipla ? 'disabled' : ''; ?>>
|
||||
|
||||
<?php if (!$isSceltaMultipla): ?>
|
||||
<option value="">Select Option</option>
|
||||
|
||||
<?php if ($sourceType === 'XLS'): ?>
|
||||
<option value="xls" <?php echo ($mappingValue === 'xls') ? 'selected' : ''; ?>>Map to XLS Column</option>
|
||||
<?php elseif ($sourceType === 'API'): ?>
|
||||
<option value="json" <?php echo ($mappingValue === 'json') ? 'selected' : ''; ?>>Map to JSON Node</option>
|
||||
<?php endif; ?>
|
||||
|
||||
<option value="auto" <?php echo ($mappingValue === 'auto') ? 'selected' : ''; ?>>Auto value</option>
|
||||
<?php endif; ?>
|
||||
|
||||
@@ -624,68 +623,40 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
<td><?php echo htmlspecialchars($fm['data_type']); ?></td>
|
||||
|
||||
<td>
|
||||
<?php
|
||||
$fixedDefaultSource = $fm['default_source'] ?? 'manual';
|
||||
$fixedJsonNode = $fm['json_node'] ?? '';
|
||||
$showFixedManual = ($fixedDefaultSource !== 'json');
|
||||
$showFixedJson = ($fixedDefaultSource === 'json');
|
||||
?>
|
||||
|
||||
<?php if ($sourceType === 'API'): ?>
|
||||
<select class="form-select fixed-source-select"
|
||||
<?php if ($fm['data_type'] === 'DATE'): ?>
|
||||
|
||||
<input type="date"
|
||||
class="form-control fixed-default-input"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
style="margin-bottom:6px;">
|
||||
<option value="manual" <?php echo ($fixedDefaultSource === 'manual') ? 'selected' : ''; ?>>
|
||||
Manual value
|
||||
</option>
|
||||
<option value="json" <?php echo ($fixedDefaultSource === 'json') ? 'selected' : ''; ?>>
|
||||
Map to JSON Node
|
||||
</option>
|
||||
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||
|
||||
<?php elseif (in_array($fm['fixed_field_key'], [
|
||||
'ClienteResponsabile',
|
||||
'ClienteFornitore',
|
||||
'ClienteAnalisi',
|
||||
'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService'
|
||||
])): ?>
|
||||
|
||||
<select class="form-select fixed-default-select"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
data-fixed-key="<?php echo htmlspecialchars($fm['fixed_field_key']); ?>"
|
||||
data-current-value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
|
||||
<select class="form-select fixed-json-node-select"
|
||||
<?php else: ?>
|
||||
|
||||
<input type="text"
|
||||
class="form-control fixed-default-input"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
data-current-json="<?php echo htmlspecialchars($fixedJsonNode, ENT_QUOTES, 'UTF-8'); ?>"
|
||||
style="display:<?php echo $showFixedJson ? 'block' : 'none'; ?>; margin-bottom:6px;">
|
||||
</select>
|
||||
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="fixed-manual-wrapper"
|
||||
style="display:<?php echo $showFixedManual ? 'block' : 'none'; ?>;">
|
||||
|
||||
<?php if ($fm['data_type'] === 'DATE'): ?>
|
||||
|
||||
<input type="date"
|
||||
class="form-control fixed-default-input"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||
|
||||
<?php elseif (in_array($fm['fixed_field_key'], [
|
||||
'ClienteResponsabile',
|
||||
'ClienteFornitore',
|
||||
'ClienteAnalisi',
|
||||
'MoltiplicatorePrezzo',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService'
|
||||
])): ?>
|
||||
|
||||
<select class="form-select fixed-default-select"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
data-fixed-key="<?php echo htmlspecialchars($fm['fixed_field_key']); ?>"
|
||||
data-current-value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||
<option value="">Loading...</option>
|
||||
</select>
|
||||
|
||||
<?php else: ?>
|
||||
|
||||
<input type="text"
|
||||
class="form-control fixed-default-input"
|
||||
data-fixed-id="<?php echo (int)$fm['id']; ?>"
|
||||
value="<?php echo htmlspecialchars($fm['default_value'] ?? ''); ?>">
|
||||
|
||||
<?php endif; ?>
|
||||
|
||||
</div>
|
||||
</td>
|
||||
|
||||
</tr>
|
||||
@@ -1221,46 +1192,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
initSelect2ForJsonDropdowns();
|
||||
}
|
||||
|
||||
function updateFixedJsonDropdowns() {
|
||||
document.querySelectorAll('select.fixed-json-node-select').forEach(select => {
|
||||
let currentValue = select.value || select.dataset.currentJson || '';
|
||||
|
||||
let options = availableJsonNodes
|
||||
.map(node => {
|
||||
const clean = String(node || '').trim();
|
||||
if (!clean) return '';
|
||||
|
||||
const info = jsonNodeLabels[clean] || {};
|
||||
const shortName = info.shortName || getLastJsonNodeName(clean);
|
||||
const sample = info.sample || '';
|
||||
|
||||
let label = shortName;
|
||||
if (sample) {
|
||||
label += ` — ${sample}`;
|
||||
}
|
||||
|
||||
const isSelected = clean === currentValue ? 'selected' : '';
|
||||
|
||||
return `
|
||||
<option
|
||||
value="${escapeHtmlAttr(clean)}"
|
||||
data-short-name="${escapeHtmlAttr(shortName)}"
|
||||
data-sample="${escapeHtmlAttr(sample)}"
|
||||
data-full-path="${escapeHtmlAttr(clean)}"
|
||||
${isSelected}>
|
||||
${escapeHtmlText(label)}
|
||||
</option>
|
||||
`;
|
||||
})
|
||||
.join('');
|
||||
|
||||
select.innerHTML = '<option value="">Select JSON Node</option>' + options;
|
||||
select.dataset.currentJson = currentValue;
|
||||
});
|
||||
|
||||
initSelect2ForFixedJsonDropdowns();
|
||||
}
|
||||
|
||||
function initSelect2ForJsonDropdowns() {
|
||||
if (!(window.jQuery && $.fn.select2)) return;
|
||||
|
||||
@@ -1344,81 +1275,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
});
|
||||
}
|
||||
|
||||
function initSelect2ForFixedJsonDropdowns() {
|
||||
if (!(window.jQuery && $.fn.select2)) return;
|
||||
|
||||
$('.fixed-json-node-select').each(function() {
|
||||
const $el = $(this);
|
||||
|
||||
if (this.style.display === 'none') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ($el.hasClass('select2-hidden-accessible')) {
|
||||
$el.select2('destroy');
|
||||
}
|
||||
|
||||
$el.select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select JSON Node',
|
||||
allowClear: true,
|
||||
|
||||
templateResult: function(data) {
|
||||
if (!data.id) return data.text;
|
||||
|
||||
const option = data.element;
|
||||
const shortName = option.getAttribute('data-short-name') || data.text;
|
||||
const sample = option.getAttribute('data-sample') || '';
|
||||
|
||||
const $row = $('<span class="select2-json-row"></span>');
|
||||
$row.append(`<span class="select2-json-node">${escapeHtmlText(shortName)}</span>`);
|
||||
|
||||
if (sample) {
|
||||
$row.append(`<span class="select2-json-value">${escapeHtmlText(sample)}</span>`);
|
||||
}
|
||||
|
||||
return $row;
|
||||
},
|
||||
|
||||
templateSelection: function(data) {
|
||||
if (!data.id) return data.text;
|
||||
|
||||
const option = data.element;
|
||||
const shortName = option.getAttribute('data-short-name') || data.text;
|
||||
const sample = option.getAttribute('data-sample') || '';
|
||||
|
||||
if (sample) {
|
||||
return `${shortName} — ${sample}`;
|
||||
}
|
||||
|
||||
return shortName;
|
||||
},
|
||||
|
||||
matcher: function(params, data) {
|
||||
if ($.trim(params.term) === '') {
|
||||
return data;
|
||||
}
|
||||
|
||||
const term = params.term.toLowerCase();
|
||||
const option = data.element;
|
||||
|
||||
const fullPath = option?.getAttribute('data-full-path')?.toLowerCase() || '';
|
||||
const shortName = option?.getAttribute('data-short-name')?.toLowerCase() || '';
|
||||
const sample = option?.getAttribute('data-sample')?.toLowerCase() || '';
|
||||
|
||||
if (
|
||||
fullPath.includes(term) ||
|
||||
shortName.includes(term) ||
|
||||
sample.includes(term)
|
||||
) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function saveJsonNodes(sampleJson, nodes) {
|
||||
return fetch('update_api_json_nodes.php', {
|
||||
@@ -1563,7 +1420,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
availableJsonNodes = nodes;
|
||||
usedJsonNodesFromDB = [];
|
||||
updateJsonDropdowns();
|
||||
updateFixedJsonDropdowns();
|
||||
|
||||
try {
|
||||
const data = await saveJsonNodes(rawJson, nodes);
|
||||
@@ -1709,12 +1565,9 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
return;
|
||||
}
|
||||
|
||||
const mappingSelect = tr?.querySelector('.mapping-select');
|
||||
const currentMappingType = mappingSelect ? mappingSelect.value : 'manual';
|
||||
|
||||
saveMapping(
|
||||
mappingId,
|
||||
currentMappingType || 'manual',
|
||||
'manual',
|
||||
el.value,
|
||||
xlsSelect ? xlsSelect.value : null,
|
||||
null,
|
||||
@@ -1744,12 +1597,9 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
return;
|
||||
}
|
||||
|
||||
const mappingSelect = tr?.querySelector('.mapping-select');
|
||||
const currentMappingType = mappingSelect ? mappingSelect.value : 'manual';
|
||||
|
||||
saveMapping(
|
||||
mappingId,
|
||||
currentMappingType || 'manual',
|
||||
'manual',
|
||||
el.value,
|
||||
xlsSelect ? xlsSelect.value : null,
|
||||
null,
|
||||
@@ -1833,7 +1683,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
|
||||
const removeBtn = tr.querySelector('.remove-xls');
|
||||
const removeJsonBtn = tr.querySelector('.remove-json');
|
||||
const isSceltaMultipla = mappingSelect.dataset.isSceltaMultipla === '1';
|
||||
|
||||
function destroyJsonSelect2() {
|
||||
if (jsonSelect && window.jQuery && $(jsonSelect).hasClass('select2-hidden-accessible')) {
|
||||
@@ -1876,12 +1725,8 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
if (autoSelect) autoSelect.style.display = 'none';
|
||||
|
||||
if (manualInput) {
|
||||
if (isSceltaMultipla) {
|
||||
manualInput.style.display = 'block';
|
||||
} else {
|
||||
manualInput.style.display = 'none';
|
||||
manualInput.value = '';
|
||||
}
|
||||
manualInput.style.display = 'none';
|
||||
manualInput.value = '';
|
||||
}
|
||||
|
||||
if (mappedColumn) mappedColumn.style.display = 'none';
|
||||
@@ -2038,7 +1883,74 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
document.querySelectorAll('#schemaFieldsBody .main-field-checkbox').forEach(cb => {
|
||||
cb.dataset.originalChecked = cb.checked ? 'true' : 'false';
|
||||
});
|
||||
// =======================
|
||||
// SAVE: Main / Import / Parts checkboxes
|
||||
// =======================
|
||||
function saveMappingFlag(checkboxEl, field) {
|
||||
const mappingId = checkboxEl.getAttribute('data-mapping-id');
|
||||
const checked = checkboxEl.checked;
|
||||
|
||||
fetch('update_mapping_flag.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: mappingId,
|
||||
field: field,
|
||||
value: checked ? 1 : 0
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
console.error("❌ Error saving flag:", field, data.message);
|
||||
|
||||
if (data.limit_reached) {
|
||||
alert(data.message);
|
||||
}
|
||||
|
||||
// Revert: il server ha rifiutato, riporto la checkbox allo stato precedente
|
||||
checkboxEl.checked = !checked;
|
||||
return;
|
||||
}
|
||||
|
||||
console.log("✅ Flag saved:", field, "=", checked ? 1 : 0, "for id", mappingId);
|
||||
|
||||
// Se il server ha deselezionato un'altra riga (caso "parts"), rifletto anche in UI
|
||||
if (data.unchecked_others && field === 'is_visible_parts') {
|
||||
document.querySelectorAll('#schemaFieldsBody .visible-parts-checkbox').forEach(cb => {
|
||||
if (cb !== checkboxEl) {
|
||||
cb.checked = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
console.error("❌ Fetch error saving flag:", field, error);
|
||||
checkboxEl.checked = !checked;
|
||||
});
|
||||
}
|
||||
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
const el = event.target;
|
||||
|
||||
if (el.classList.contains('main-field-checkbox')) {
|
||||
saveMappingFlag(el, 'main_field');
|
||||
}
|
||||
|
||||
if (el.classList.contains('visible-import-checkbox')) {
|
||||
saveMappingFlag(el, 'is_visible_import');
|
||||
}
|
||||
|
||||
if (el.classList.contains('visible-parts-checkbox')) {
|
||||
saveMappingFlag(el, 'is_visible_parts');
|
||||
}
|
||||
|
||||
if (el.classList.contains('addnote-checkbox')) {
|
||||
saveMappingFlag(el, 'is_addnote');
|
||||
}
|
||||
});
|
||||
// AUTO VALUE select change -> save auto_value
|
||||
document.getElementById('schemaFieldsBody').addEventListener('change', function(event) {
|
||||
if (!event.target.classList.contains('auto-value-select')) return;
|
||||
@@ -2122,19 +2034,13 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
let mappingId = event.target.getAttribute('data-id');
|
||||
let xlsSelect = tr.querySelector('.xls-columns');
|
||||
let jsonSelect = tr.querySelector('.json-nodes');
|
||||
let mappingSelect = tr.querySelector('.mapping-select');
|
||||
|
||||
let currentMappingType = mappingSelect ? mappingSelect.value : 'manual';
|
||||
|
||||
console.log("Manual default dropdown changed:", {
|
||||
id: mappingId,
|
||||
value: event.target.value,
|
||||
mappingType: currentMappingType
|
||||
value: event.target.value
|
||||
});
|
||||
|
||||
saveMapping(
|
||||
mappingId,
|
||||
currentMappingType || 'manual',
|
||||
'manual',
|
||||
event.target.value,
|
||||
xlsSelect ? xlsSelect.value : null,
|
||||
null,
|
||||
@@ -2233,7 +2139,7 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
mapping_type: mappingType,
|
||||
excel_column: mappingType === 'xls' ? excelColumn : null,
|
||||
json_node: mappingType === 'json' ? jsonNode : null,
|
||||
manual_default: defaultValue,
|
||||
manual_default: mappingType === 'manual' ? defaultValue : null,
|
||||
auto_value: mappingType === 'auto' ? (autoValue || 'none') : 'none',
|
||||
tablename: "<?php echo $template['target_table']; ?>"
|
||||
})
|
||||
@@ -2262,115 +2168,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
// FIXED FIELDS (DB autosave)
|
||||
// =======================
|
||||
|
||||
// =======================
|
||||
// FIXED FIELDS: source manual/json autosave
|
||||
// Only used for API/JSON templates
|
||||
// =======================
|
||||
document.addEventListener('change', function(e) {
|
||||
if (!e.target.classList.contains('fixed-source-select')) return;
|
||||
|
||||
const sourceSelect = e.target;
|
||||
const fixedId = sourceSelect.dataset.fixedId;
|
||||
const tr = sourceSelect.closest('tr');
|
||||
|
||||
const manualWrapper = tr.querySelector('.fixed-manual-wrapper');
|
||||
const jsonSelect = tr.querySelector('.fixed-json-node-select');
|
||||
|
||||
if (sourceSelect.value === 'json') {
|
||||
if (manualWrapper) {
|
||||
manualWrapper.style.display = 'none';
|
||||
}
|
||||
|
||||
if (jsonSelect) {
|
||||
jsonSelect.style.display = 'block';
|
||||
updateFixedJsonDropdowns();
|
||||
}
|
||||
} else {
|
||||
if (jsonSelect) {
|
||||
if (window.jQuery && $(jsonSelect).hasClass('select2-hidden-accessible')) {
|
||||
$(jsonSelect).select2('destroy');
|
||||
}
|
||||
|
||||
jsonSelect.value = '';
|
||||
jsonSelect.dataset.currentJson = '';
|
||||
jsonSelect.style.display = 'none';
|
||||
}
|
||||
|
||||
if (manualWrapper) {
|
||||
manualWrapper.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
fetch('update_fixed_field.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: fixedId,
|
||||
field: 'default_source',
|
||||
value: sourceSelect.value
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
throw new Error(data.message || 'Update failed');
|
||||
}
|
||||
|
||||
fixedStatus('✅ Saved');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
fixedStatus('❌ Save error', true);
|
||||
});
|
||||
});
|
||||
|
||||
// =======================
|
||||
// FIXED FIELDS: JSON node autosave
|
||||
// =======================
|
||||
function saveFixedJsonNode(selectEl) {
|
||||
const fixedId = selectEl.dataset.fixedId;
|
||||
const jsonNode = selectEl.value || '';
|
||||
|
||||
fetch('update_fixed_field.php', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
id: fixedId,
|
||||
field: 'json_node',
|
||||
value: jsonNode
|
||||
})
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (!data.success) {
|
||||
throw new Error(data.message || 'Update failed');
|
||||
}
|
||||
|
||||
selectEl.dataset.currentJson = jsonNode;
|
||||
fixedStatus('✅ Saved');
|
||||
})
|
||||
.catch(error => {
|
||||
console.error(error);
|
||||
fixedStatus('❌ Save error', true);
|
||||
});
|
||||
}
|
||||
|
||||
document.addEventListener('change', function(e) {
|
||||
if (!e.target.classList.contains('fixed-json-node-select')) return;
|
||||
|
||||
saveFixedJsonNode(e.target);
|
||||
});
|
||||
|
||||
if (window.jQuery) {
|
||||
$(document).on('select2:select select2:clear', '.fixed-json-node-select', function() {
|
||||
saveFixedJsonNode(this);
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str ?? '')
|
||||
.replaceAll('&', '&')
|
||||
@@ -2399,11 +2196,11 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
'ClienteResponsabile',
|
||||
'ClienteFornitore',
|
||||
'ClienteAnalisi',
|
||||
'ClienteFatturazione', // ← AGGIUNGI QUESTA RIGA
|
||||
'MoltiplicatorePrezzo',
|
||||
'AnagraficaCertestObject',
|
||||
'AnagraficaCertestService'
|
||||
];
|
||||
|
||||
tbody.innerHTML = rows.map(r => {
|
||||
const type = String(r.data_type || '').toUpperCase();
|
||||
const dv = r.default_value ?? '';
|
||||
@@ -2607,7 +2404,6 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
}
|
||||
|
||||
updateJsonDropdowns();
|
||||
updateFixedJsonDropdowns();
|
||||
}
|
||||
|
||||
fillFixedDropdowns().then(() => {
|
||||
@@ -2708,11 +2504,22 @@ $apiSampleJson = $template['api_sample_json'] ?? '';
|
||||
})).filter(x => x.id != null && x.label);
|
||||
}
|
||||
|
||||
if (key === 'ClienteFornitore' || key === 'ClienteAnalisi') {
|
||||
if (key === 'ClienteFornitore' || key === 'ClienteAnalisi' || key === 'ClienteFatturazione') {
|
||||
const j = await fetchJson('get_clienti.php');
|
||||
const list = normalizeList(j, key);
|
||||
|
||||
return list.map(x => ({
|
||||
const isInattivo = (x) => {
|
||||
const v = x.Inattivo;
|
||||
return v === true || v === 1 || v === '1' || v === 'true';
|
||||
};
|
||||
|
||||
// Fornitore e Fatturazione: solo clienti ATTIVI (Inattivo = false)
|
||||
// Analisi: solo clienti INATTIVI (Inattivo = true)
|
||||
const filtered = (key === 'ClienteAnalisi') ?
|
||||
list.filter(isInattivo) :
|
||||
list.filter(x => !isInattivo(x));
|
||||
|
||||
return filtered.map(x => ({
|
||||
id: x.IdCliente ?? x.Id ?? x.id,
|
||||
label: formatClientLabel(x)
|
||||
})).filter(x => x.id != null && x.label);
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,106 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* ONE-OFF MIGRATION
|
||||
* Aggiunge la riga ClienteFatturazione a tutti i template che hanno
|
||||
* già dei fixed fields (quindi hanno già usato "Add Fixed Fields")
|
||||
* ma non hanno ancora ClienteFatturazione.
|
||||
*
|
||||
* Idempotente: si può rilanciare più volte senza creare duplicati.
|
||||
*
|
||||
* Uso:
|
||||
* - Dry run (default): migrate_add_fatturazione.php
|
||||
* - Esecuzione reale: migrate_add_fatturazione.php?confirm=1
|
||||
*/
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$confirm = isset($_GET['confirm']) && $_GET['confirm'] == '1';
|
||||
|
||||
// Stessi default usati da create_fixed_fields.php per ClienteFatturazione
|
||||
$FIELD_KEY = 'ClienteFatturazione';
|
||||
$DATA_TYPE = 'INT';
|
||||
$IS_MANUAL = 1;
|
||||
$IS_REQUIRED = 1;
|
||||
$DEFAULT_VAL = null;
|
||||
$IS_VISIBLE = 1;
|
||||
|
||||
try {
|
||||
// 1. Tutti i template_id distinti che hanno già fixed fields
|
||||
$stmt = $pdo->query("SELECT DISTINCT template_id FROM template_fixed_mapping ORDER BY template_id ASC");
|
||||
$allTemplateIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
// 2. Tra questi, quelli che NON hanno già ClienteFatturazione
|
||||
$placeholders = implode(',', array_fill(0, count($allTemplateIds), '?'));
|
||||
$missingTemplateIds = $allTemplateIds;
|
||||
|
||||
if (!empty($allTemplateIds)) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT DISTINCT template_id
|
||||
FROM template_fixed_mapping
|
||||
WHERE fixed_field_key = ?
|
||||
AND template_id IN ({$placeholders})
|
||||
");
|
||||
$stmt->execute(array_merge([$FIELD_KEY], $allTemplateIds));
|
||||
$alreadyHave = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
|
||||
$missingTemplateIds = array_values(array_diff($allTemplateIds, $alreadyHave));
|
||||
}
|
||||
|
||||
$report = [
|
||||
'total_templates_with_fixed_fields' => count($allTemplateIds),
|
||||
'templates_missing_fatturazione' => count($missingTemplateIds),
|
||||
'template_ids_missing' => $missingTemplateIds,
|
||||
'dry_run' => !$confirm,
|
||||
'inserted' => 0,
|
||||
];
|
||||
|
||||
if (empty($missingTemplateIds)) {
|
||||
echo json_encode(['success' => true] + $report, JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (!$confirm) {
|
||||
// Dry run: mostra solo cosa farebbe, non inserisce nulla
|
||||
echo json_encode(['success' => true] + $report, JSON_PRETTY_PRINT);
|
||||
exit;
|
||||
}
|
||||
|
||||
// 3. Esecuzione reale
|
||||
$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, :is_manual, :data_type, :is_required, :default_value, :is_visible_import)
|
||||
");
|
||||
|
||||
$insertedCount = 0;
|
||||
foreach ($missingTemplateIds as $tid) {
|
||||
$ins->execute([
|
||||
':template_id' => $tid,
|
||||
':fixed_field_key' => $FIELD_KEY,
|
||||
':is_manual' => $IS_MANUAL,
|
||||
':data_type' => $DATA_TYPE,
|
||||
':is_required' => $IS_REQUIRED,
|
||||
':default_value' => $DEFAULT_VAL,
|
||||
':is_visible_import' => $IS_VISIBLE,
|
||||
]);
|
||||
$insertedCount++;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
$report['inserted'] = $insertedCount;
|
||||
|
||||
echo json_encode(['success' => true] + $report, JSON_PRETTY_PRINT);
|
||||
} catch (Exception $e) {
|
||||
if ($pdo->inTransaction()) $pdo->rollBack();
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -57,6 +57,7 @@
|
||||
style="width: 34px; height: 28px; padding: 1px; border: 1px solid #ccc; border-radius: 4px; cursor: pointer;">
|
||||
</div>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="addDescriptionsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Aggiungi Lista Descrizioni</button>
|
||||
<button type="button" class="btn btn-outline-dark btn-sm" id="addFreeTextBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;"><i class="fas fa-font"></i> Testo Libero</button>
|
||||
<button type="button" class="btn btn-danger btn-sm" id="removeAnnotationsBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Rimuovi Descrizioni</button>
|
||||
<button type="button" class="btn btn-warning btn-sm" id="undoMarkerBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Undo Marker</button>
|
||||
<button type="button" class="btn btn-success btn-sm" id="savePhotoBtnAnnotations" style="padding: 0.1rem 0.5rem; font-size: 0.8rem;">Salva Foto con Nome</button>
|
||||
@@ -280,5 +281,11 @@
|
||||
margin: 2px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
#addFreeTextBtnAnnotations.active {
|
||||
background-color: #343a40 !important;
|
||||
border-color: #343a40 !important;
|
||||
color: #fff !important;
|
||||
}
|
||||
</style>
|
||||
<script src="https://code.jquery.com/ui/1.13.2/jquery-ui.min.js"></script>
|
||||
@@ -29,6 +29,9 @@
|
||||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 10px; min-width: 0;">
|
||||
<h6 style="margin: 0; white-space: nowrap;">Elenco Parti</h6>
|
||||
<div style="display: flex; align-items: center; min-width: 0; gap: 8px;">
|
||||
<button type="button" id="savePartsBtn" class="btn btn-success btn-sm">
|
||||
<i class="fas fa-save"></i> Salva
|
||||
</button>
|
||||
<button type="button"
|
||||
class="btn btn-dark btn-sm open-analysis-modal-btn"
|
||||
id="openAnalysisModalBtn"
|
||||
@@ -188,6 +191,37 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modale di conferma per la clonazione delle parti -->
|
||||
<div class="modal fade" id="cloneConfirmModal" tabindex="-1" aria-labelledby="cloneConfirmModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-sm">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="cloneConfirmModalLabel">Conferma Clonazione</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<p class="clone-confirm-message mb-3"></p>
|
||||
<div class="form-check mb-2">
|
||||
<input class="form-check-input" type="checkbox" id="cloneNotesCheckbox" checked>
|
||||
<label class="form-check-label" for="cloneNotesCheckbox">
|
||||
Duplica note
|
||||
</label>
|
||||
</div>
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="checkbox" id="cloneAnalysesCheckbox">
|
||||
<label class="form-check-label" for="cloneAnalysesCheckbox">
|
||||
Duplica anche le analisi collegate
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary btn-sm" data-bs-dismiss="modal">Annulla</button>
|
||||
<button type="button" class="btn btn-primary btn-sm" id="cloneConfirmBtn">Conferma</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Finestra modale "Aggiungi preventivo" -->
|
||||
<div class="modal fade" id="addQuotationModal" tabindex="-1" aria-labelledby="addQuotationModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-md">
|
||||
@@ -208,6 +242,23 @@
|
||||
</div>
|
||||
|
||||
<style>
|
||||
#partsModalLabel {
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#trfHeader {
|
||||
font-size: 1rem;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* --- Base --- */
|
||||
#partsModal {
|
||||
z-index: 1060 !important
|
||||
}
|
||||
|
||||
/* --- Base --- */
|
||||
#partsModal {
|
||||
z-index: 1060 !important
|
||||
@@ -738,5 +789,13 @@
|
||||
padding: 0.1rem 0.25rem !important;
|
||||
}
|
||||
|
||||
#cloneConfirmModal {
|
||||
z-index: 1070 !important
|
||||
}
|
||||
|
||||
#cloneConfirmModal .modal-backdrop {
|
||||
z-index: 1065 !important
|
||||
}
|
||||
|
||||
/* rosso */
|
||||
</style>
|
||||
+392
-510
File diff suppressed because it is too large
Load Diff
@@ -53,7 +53,143 @@ function normalizeColumnIndex($value): int
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim a cell value treating invisible/Unicode spaces as empty.
|
||||
* PHP's native trim() strips only ASCII whitespace (" \t\n\r\0\x0B"), so a cell
|
||||
* that contains only a non-breaking space (U+00A0), zero-width space (U+200B),
|
||||
* BOM (U+FEFF) or another Unicode space looks blank to a human but would still
|
||||
* count as "filled" — pulling a ghost column into the import or retaining a
|
||||
* visually-empty row. Normalize those to a real space before trimming.
|
||||
*/
|
||||
function cleanCellText($value): string
|
||||
{
|
||||
$raw = (string)$value;
|
||||
$cleaned = preg_replace(
|
||||
'/[\x{00A0}\x{200B}\x{FEFF}\x{2000}-\x{200A}\x{202F}\x{205F}\x{3000}]+/u',
|
||||
' ',
|
||||
$raw
|
||||
);
|
||||
|
||||
// preg_replace returns null on malformed UTF-8; fall back to the raw value.
|
||||
return trim($cleaned ?? $raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* Pre-clean an .xlsx by streaming out "ghost" cells: empty, self-closing
|
||||
* <c .../> (and <c ...></c>) elements that carry only leftover styling.
|
||||
*/
|
||||
function slimXlsxGhostCells(string $path): ?string
|
||||
{
|
||||
if (!class_exists('ZipArchive')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$slim = $path . '.slim.xlsx';
|
||||
|
||||
try {
|
||||
if (!copy($path, $slim)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Phase 1: stream-strip each worksheet to a temp file (low memory).
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($slim) !== true) {
|
||||
@unlink($slim);
|
||||
return null;
|
||||
}
|
||||
|
||||
$temps = [];
|
||||
for ($i = 0; $i < $zip->numFiles; $i++) {
|
||||
$name = $zip->getNameIndex($i);
|
||||
if (!preg_match('#^xl/worksheets/sheet\d+\.xml$#', $name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$in = $zip->getStream($name);
|
||||
if (!$in) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$tmp = tempnam(sys_get_temp_dir(), 'slim');
|
||||
$out = fopen($tmp, 'w');
|
||||
$carry = '';
|
||||
|
||||
while (!feof($in)) {
|
||||
$chunk = fread($in, 4194304);
|
||||
if ($chunk === '' || $chunk === false) {
|
||||
break;
|
||||
}
|
||||
// Only process up to the last complete '>' so a cell tag is
|
||||
// never split across a chunk boundary; carry the remainder.
|
||||
$buf = $carry . $chunk;
|
||||
$lastGt = strrpos($buf, '>');
|
||||
if ($lastGt === false) {
|
||||
$carry = $buf;
|
||||
continue;
|
||||
}
|
||||
$proc = substr($buf, 0, $lastGt + 1);
|
||||
$carry = substr($buf, $lastGt + 1);
|
||||
$proc = preg_replace(['#<c [^>]*/>#', '#<c [^>]*></c>#'], '', $proc);
|
||||
fwrite($out, $proc);
|
||||
}
|
||||
if ($carry !== '') {
|
||||
fwrite($out, $carry);
|
||||
}
|
||||
fclose($in);
|
||||
fclose($out);
|
||||
$temps[$name] = $tmp;
|
||||
}
|
||||
$zip->close();
|
||||
|
||||
if (!$temps) {
|
||||
@unlink($slim);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Phase 2: swap the stripped worksheets back into the archive.
|
||||
$zip = new ZipArchive();
|
||||
if ($zip->open($slim) !== true) {
|
||||
foreach ($temps as $t) {
|
||||
@unlink($t);
|
||||
}
|
||||
@unlink($slim);
|
||||
return null;
|
||||
}
|
||||
foreach ($temps as $name => $tmp) {
|
||||
$zip->deleteName($name);
|
||||
$zip->addFile($tmp, $name);
|
||||
}
|
||||
$zip->close(); // addFile streams from disk here, so unlink only after.
|
||||
|
||||
foreach ($temps as $t) {
|
||||
@unlink($t);
|
||||
}
|
||||
|
||||
return $slim;
|
||||
} catch (\Throwable $e) {
|
||||
error_log('slimXlsxGhostCells failed: ' . $e->getMessage());
|
||||
@unlink($slim);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
// Quando il body POST supera post_max_size, PHP scarta $_POST e $_FILES
|
||||
// (warning "Content-Length exceeds the limit ... in Unknown on line 0") e lo
|
||||
// script riceve una richiesta vuota. Lo intercettiamo per dare un messaggio
|
||||
// chiaro invece di "Richiesta non valida".
|
||||
if (
|
||||
$_SERVER['REQUEST_METHOD'] === 'POST'
|
||||
&& empty($_POST) && empty($_FILES)
|
||||
&& (int)($_SERVER['CONTENT_LENGTH'] ?? 0) > 0
|
||||
) {
|
||||
$postMax = ini_get('post_max_size');
|
||||
throw new Exception(
|
||||
"Il file caricato supera il limite di upload del server (post_max_size = {$postMax}). " .
|
||||
"Chiedi all'amministratore di aumentare post_max_size e upload_max_filesize."
|
||||
);
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['excel_file'])) {
|
||||
$template_id = isset($_POST['template_id']) ? intval($_POST['template_id']) : 0;
|
||||
|
||||
@@ -70,7 +206,7 @@ try {
|
||||
* Così non dipendiamo solo dal form e siamo sicuri di usare i dati salvati.
|
||||
*/
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
SELECT
|
||||
id,
|
||||
header_row,
|
||||
start_column,
|
||||
@@ -142,14 +278,14 @@ try {
|
||||
|
||||
// Recupera il mapping da template_mapping
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT
|
||||
field_id AS excel_column,
|
||||
field_id AS mysql_column,
|
||||
data_type,
|
||||
is_required,
|
||||
default_value,
|
||||
is_manual
|
||||
FROM template_mapping
|
||||
SELECT
|
||||
field_id AS excel_column,
|
||||
field_id AS mysql_column,
|
||||
data_type,
|
||||
is_required,
|
||||
default_value,
|
||||
is_manual
|
||||
FROM template_mapping
|
||||
WHERE template_id = ?
|
||||
");
|
||||
$stmt->execute([$template_id]);
|
||||
@@ -161,8 +297,28 @@ try {
|
||||
if (empty($mappings)) {
|
||||
$response['error'] = "Nessun mapping trovato per il template con ID $template_id";
|
||||
} else {
|
||||
// Carica il file rinominato con PHPSpreadsheet
|
||||
$spreadsheet = IOFactory::load($destination);
|
||||
// Pre-clean ghost cells for .xlsx so a bloated worksheet (millions
|
||||
// of empty styled cells) doesn't make the load time out. Falls back
|
||||
// to the original file if slimming fails for any reason.
|
||||
$loadPath = $destination;
|
||||
$slimPath = null;
|
||||
if (preg_match('/\.xlsx$/i', $destination)) {
|
||||
$slimPath = slimXlsxGhostCells($destination);
|
||||
if ($slimPath !== null) {
|
||||
$loadPath = $slimPath;
|
||||
error_log("Ghost-cell pre-clean applied, loading slimmed copy: $slimPath");
|
||||
}
|
||||
}
|
||||
|
||||
// Carica il file con PHPSpreadsheet.
|
||||
$reader = IOFactory::createReaderForFile($loadPath);
|
||||
$reader->setReadEmptyCells(false);
|
||||
$spreadsheet = $reader->load($loadPath);
|
||||
|
||||
// The slimmed copy is only needed for parsing; drop it now.
|
||||
if ($slimPath !== null) {
|
||||
@unlink($slimPath);
|
||||
}
|
||||
|
||||
$sheetCount = $spreadsheet->getSheetCount();
|
||||
$sheetNames = $spreadsheet->getSheetNames();
|
||||
@@ -189,8 +345,8 @@ try {
|
||||
|
||||
error_log("Selected XLS sheet - index: {$xlsSheetIndex}, name: {$selectedSheetName}");
|
||||
|
||||
$highestRow = $worksheet->getHighestRow();
|
||||
$highestColumn = $worksheet->getHighestColumn();
|
||||
$highestRow = $worksheet->getHighestDataRow();
|
||||
$highestColumn = $worksheet->getHighestDataColumn();
|
||||
$highestColumnIndex = Coordinate::columnIndexFromString($highestColumn);
|
||||
|
||||
$startRow = max(1, $header_row);
|
||||
@@ -199,7 +355,7 @@ try {
|
||||
// Advance startColumn to first non-empty cell in header row, matching JS behavior
|
||||
for ($sc = $startColumn; $sc <= $highestColumnIndex; $sc++) {
|
||||
$cl = Coordinate::stringFromColumnIndex($sc);
|
||||
$cv = trim((string)($worksheet->getCell($cl . $header_row)->getCalculatedValue() ?? ''));
|
||||
$cv = cleanCellText($worksheet->getCell($cl . $header_row)->getCalculatedValue() ?? '');
|
||||
|
||||
if ($cv !== '') {
|
||||
$startColumn = $sc;
|
||||
@@ -207,6 +363,19 @@ try {
|
||||
}
|
||||
}
|
||||
|
||||
$lastHeaderCol = $startColumn;
|
||||
for ($hc = $startColumn; $hc <= $highestColumnIndex; $hc++) {
|
||||
$hl = Coordinate::stringFromColumnIndex($hc);
|
||||
$hv = cleanCellText($worksheet->getCell($hl . $header_row)->getCalculatedValue() ?? '');
|
||||
|
||||
if ($hv !== '') {
|
||||
$lastHeaderCol = $hc;
|
||||
}
|
||||
}
|
||||
|
||||
$highestColumnIndex = $lastHeaderCol;
|
||||
$highestColumn = Coordinate::stringFromColumnIndex($highestColumnIndex);
|
||||
|
||||
// Debug dei parametri
|
||||
error_log(
|
||||
"Processing - template_id: $template_id, " .
|
||||
@@ -269,7 +438,7 @@ try {
|
||||
|
||||
$columnLetter = Coordinate::stringFromColumnIndex($physCol);
|
||||
$cell = $worksheet->getCell($columnLetter . $header_row);
|
||||
$cellValue = trim((string)($cell ? $cell->getCalculatedValue() : ''));
|
||||
$cellValue = cleanCellText($cell ? $cell->getCalculatedValue() : '');
|
||||
$cellValue = preg_replace('/[\r\n\t]+/', ' ', $cellValue);
|
||||
|
||||
// Empty headers get __empty_N__ to match mapping page
|
||||
@@ -306,7 +475,7 @@ try {
|
||||
$filledCount = 0;
|
||||
|
||||
foreach ($headerFilledIndices as $idx) {
|
||||
if (isset($rowData[$idx]) && trim((string)$rowData[$idx]) !== '') {
|
||||
if (isset($rowData[$idx]) && cleanCellText($rowData[$idx]) !== '') {
|
||||
$filledCount++;
|
||||
}
|
||||
}
|
||||
@@ -322,13 +491,13 @@ try {
|
||||
// Recupera routine dal template
|
||||
if ($template && !empty($template['idroutine'])) {
|
||||
$stmtRoutine = $pdo->prepare("
|
||||
SELECT
|
||||
idroutine,
|
||||
name,
|
||||
filename,
|
||||
headerrow,
|
||||
instruction
|
||||
FROM routine
|
||||
SELECT
|
||||
idroutine,
|
||||
name,
|
||||
filename,
|
||||
headerrow,
|
||||
instruction
|
||||
FROM routine
|
||||
WHERE idroutine = ?
|
||||
");
|
||||
$stmtRoutine->execute([$template['idroutine']]);
|
||||
|
||||
@@ -1,700 +0,0 @@
|
||||
<?php include('include/headscript.php'); ?>
|
||||
<!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/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
<link href="assets/plugins/datatable/css/dataTables.bootstrap5.min.css" rel="stylesheet" />
|
||||
|
||||
<title>TRF-Project - Customer Reports</title>
|
||||
|
||||
<style>
|
||||
.lookup-wrapper {
|
||||
width: 100%;
|
||||
max-width: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.lookup-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.lookup-subtitle {
|
||||
font-size: 13px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.compact-card .card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.table-report th {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
color: #6c757d;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.table-report td {
|
||||
font-size: 13px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.status-pill-success {
|
||||
background: #e8fff1;
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.status-pill-warning {
|
||||
background: #fff3cd;
|
||||
color: #b58100;
|
||||
}
|
||||
|
||||
.pdf-icon-link {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
border-radius: 10px;
|
||||
background: #dc3545;
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 20px;
|
||||
text-decoration: none;
|
||||
transition: all 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.pdf-icon-link:hover {
|
||||
color: #fff;
|
||||
opacity: 0.85;
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
.no-pdf {
|
||||
font-size: 12px;
|
||||
color: #adb5bd;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
border: 1px dashed #ced4da;
|
||||
border-radius: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: #6c757d;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.spinner-inline {
|
||||
display: none;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.customer-box {
|
||||
display: none;
|
||||
border: 1px solid #e9ecef;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
padding: 12px 14px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.customer-label {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: #6c757d;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.customer-value {
|
||||
font-size: 14px;
|
||||
color: #212529;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.json-preview {
|
||||
display: none;
|
||||
background: #111827;
|
||||
color: #e5e7eb;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
font-size: 12px;
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.table-responsive {
|
||||
border-radius: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-container {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single {
|
||||
height: 38px;
|
||||
border: 1px solid #ced4da;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
line-height: 36px;
|
||||
font-size: 14px;
|
||||
color: #212529;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 36px;
|
||||
}
|
||||
|
||||
.select2-dropdown {
|
||||
z-index: 9999;
|
||||
}
|
||||
</style>
|
||||
</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="lookup-wrapper">
|
||||
|
||||
<div class="card radius-10 compact-card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div>
|
||||
<div class="lookup-title">Customer Test Reports</div>
|
||||
<div class="lookup-subtitle">
|
||||
Select a VisualLims customer and retrieve the latest reports with PDF links.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form id="customerReportsForm" class="row g-3 align-items-end">
|
||||
|
||||
<div class="col-md-5">
|
||||
<label for="idCliente" class="form-label fw-semibold">Customer</label>
|
||||
<select id="idCliente" name="idCliente" class="form-select">
|
||||
<option value="">Loading customers...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<label for="limitReports" class="form-label fw-semibold">Limit</label>
|
||||
<select id="limitReports" name="limitReports" class="form-select">
|
||||
<option value="1">Last 1</option>
|
||||
<option value="3" selected>Last 3</option>
|
||||
<option value="5">Last 5</option>
|
||||
<option value="10">Last 10</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-2">
|
||||
<label for="signedStatus" class="form-label fw-semibold">Status</label>
|
||||
<select id="signedStatus" name="signedStatus" class="form-select">
|
||||
<option value="all" selected>All</option>
|
||||
<option value="signed">Signed</option>
|
||||
<option value="not_signed">Not signed</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<button type="submit" id="btnSearchReports" class="btn btn-primary w-100">
|
||||
<i class="bx bx-search"></i> Search Reports
|
||||
<span class="spinner-border spinner-border-sm spinner-inline" id="searchSpinner" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="customerBox" class="customer-box">
|
||||
<div class="row g-3">
|
||||
<div class="col-md-4">
|
||||
<div class="customer-label">Customer Code</div>
|
||||
<div class="customer-value" id="selectedCustomerCode">-</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-8">
|
||||
<div class="customer-label">Customer Name</div>
|
||||
<div class="customer-value" id="selectedCustomerName">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="resultContainer" style="display:none;">
|
||||
|
||||
<div class="card radius-10 compact-card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<h6 class="mb-0">
|
||||
Reports
|
||||
<span class="badge bg-light text-dark ms-1" id="reportCountBadge">0</span>
|
||||
</h6>
|
||||
|
||||
<button type="button" id="toggleJsonBtn" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bx bx-code-alt"></i> Show JSON
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped table-hover table-report align-middle mb-0" id="reportsTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Report Number</th>
|
||||
<th>Report ID</th>
|
||||
<th>Report Date</th>
|
||||
<th>Print Date</th>
|
||||
<th>Version</th>
|
||||
<th>Status</th>
|
||||
<th class="text-center">PDF</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="reportsTableBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<pre id="jsonPreview" class="json-preview mt-3"></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="emptyState" class="empty-state">
|
||||
<i class="bx bx-file-find" style="font-size:34px;"></i>
|
||||
<div class="mt-2 fw-semibold">No reports loaded</div>
|
||||
<div class="small">Select a customer, choose the limit and click Search Reports.</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>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script src="assets/plugins/datatable/js/jquery.dataTables.min.js"></script>
|
||||
<script src="assets/plugins/datatable/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
let lastJsonResponse = null;
|
||||
let loadedCustomers = [];
|
||||
let reportsDataTable = null;
|
||||
|
||||
function escapeHtml(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return String(value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function formatDate(value) {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return date.toLocaleString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
function setLoading(isLoading) {
|
||||
$('#btnSearchReports').prop('disabled', isLoading);
|
||||
$('#searchSpinner').toggle(isLoading);
|
||||
}
|
||||
|
||||
function resetResults() {
|
||||
lastJsonResponse = null;
|
||||
|
||||
if (reportsDataTable !== null) {
|
||||
reportsDataTable.destroy();
|
||||
reportsDataTable = null;
|
||||
}
|
||||
|
||||
$('#resultContainer').hide();
|
||||
$('#emptyState').show();
|
||||
$('#reportsTableBody').html('');
|
||||
$('#reportCountBadge').text('0');
|
||||
$('#jsonPreview').hide().text('');
|
||||
$('#toggleJsonBtn').html('<i class="bx bx-code-alt"></i> Show JSON');
|
||||
}
|
||||
|
||||
function loadCustomers() {
|
||||
$.ajax({
|
||||
url: 'get_clienti.php',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
success: function(response) {
|
||||
const select = $('#idCliente');
|
||||
select.empty();
|
||||
|
||||
const customers = response.value || response || [];
|
||||
loadedCustomers = Array.isArray(customers) ? customers : [];
|
||||
|
||||
select.append('<option value="">Select customer...</option>');
|
||||
|
||||
loadedCustomers.forEach(function(customer) {
|
||||
const idCliente = customer.IdCliente || '';
|
||||
const codiceCliente = customer.CodiceCliente || '';
|
||||
const nominativo = customer.Nominativo || '';
|
||||
|
||||
const label = codiceCliente ?
|
||||
codiceCliente + ' - ' + nominativo :
|
||||
nominativo;
|
||||
|
||||
select.append(
|
||||
'<option value="' + escapeHtml(idCliente) + '" ' +
|
||||
'data-code="' + escapeHtml(codiceCliente) + '" ' +
|
||||
'data-name="' + escapeHtml(nominativo) + '">' +
|
||||
escapeHtml(label) +
|
||||
'</option>'
|
||||
);
|
||||
});
|
||||
|
||||
if ($.fn.select2) {
|
||||
select.select2({
|
||||
width: '100%',
|
||||
placeholder: 'Search customer...',
|
||||
allowClear: true,
|
||||
minimumInputLength: 0,
|
||||
matcher: function(params, data) {
|
||||
if ($.trim(params.term) === '') {
|
||||
return data;
|
||||
}
|
||||
|
||||
if (typeof data.text === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const term = params.term.toLowerCase();
|
||||
const text = data.text.toLowerCase();
|
||||
|
||||
if (text.indexOf(term) > -1) {
|
||||
return data;
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
sorter: function(data) {
|
||||
const term = $('.select2-search__field').val();
|
||||
|
||||
if (!term) {
|
||||
return data;
|
||||
}
|
||||
|
||||
const search = term.toLowerCase();
|
||||
|
||||
return data.sort(function(a, b) {
|
||||
const aText = (a.text || '').toLowerCase();
|
||||
const bText = (b.text || '').toLowerCase();
|
||||
|
||||
const aStarts = aText.startsWith(search);
|
||||
const bStarts = bText.startsWith(search);
|
||||
|
||||
if (aStarts && !bStarts) return -1;
|
||||
if (!aStarts && bStarts) return 1;
|
||||
|
||||
return aText.localeCompare(bText);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
$('#idCliente').html('<option value="">Error loading customers</option>');
|
||||
|
||||
let message = 'Unable to load customers.';
|
||||
|
||||
if (xhr.responseJSON && xhr.responseJSON.error) {
|
||||
message = xhr.responseJSON.error;
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'Customer loading error',
|
||||
text: message,
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateSelectedCustomerBox() {
|
||||
const selectedOption = $('#idCliente option:selected');
|
||||
|
||||
const customerCode = selectedOption.data('code') || '-';
|
||||
const customerName = selectedOption.data('name') || '-';
|
||||
|
||||
if (!$('#idCliente').val()) {
|
||||
$('#customerBox').hide();
|
||||
$('#selectedCustomerCode').text('-');
|
||||
$('#selectedCustomerName').text('-');
|
||||
return;
|
||||
}
|
||||
|
||||
$('#selectedCustomerCode').text(customerCode);
|
||||
$('#selectedCustomerName').text(customerName);
|
||||
$('#customerBox').show();
|
||||
}
|
||||
|
||||
function renderStatus(isSigned) {
|
||||
if (isSigned === true) {
|
||||
return '<span class="status-pill status-pill-success"><i class="bx bx-check-circle"></i> Signed</span>';
|
||||
}
|
||||
|
||||
if (isSigned === false) {
|
||||
return '<span class="status-pill status-pill-warning"><i class="bx bx-time"></i> Not signed</span>';
|
||||
}
|
||||
|
||||
return '-';
|
||||
}
|
||||
|
||||
function renderPdfCell(pdfFiles) {
|
||||
if (!Array.isArray(pdfFiles) || pdfFiles.length === 0) {
|
||||
return '<span class="no-pdf">No PDF</span>';
|
||||
}
|
||||
|
||||
const firstPdf = pdfFiles[0];
|
||||
|
||||
if (!firstPdf.download_url) {
|
||||
return '<span class="no-pdf">No PDF</span>';
|
||||
}
|
||||
|
||||
return `
|
||||
<a href="${escapeHtml(firstPdf.download_url)}"
|
||||
target="_blank"
|
||||
class="pdf-icon-link"
|
||||
title="${escapeHtml(firstPdf.file_name || 'Download PDF')}">
|
||||
<i class="bx bxs-file-pdf"></i>
|
||||
</a>
|
||||
`;
|
||||
}
|
||||
|
||||
function initReportsDataTable() {
|
||||
if (reportsDataTable !== null) {
|
||||
reportsDataTable.destroy();
|
||||
reportsDataTable = null;
|
||||
}
|
||||
|
||||
reportsDataTable = $('#reportsTable').DataTable({
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false,
|
||||
ordering: true,
|
||||
order: [
|
||||
[2, 'desc']
|
||||
],
|
||||
autoWidth: false,
|
||||
responsive: true,
|
||||
columnDefs: [{
|
||||
targets: 6,
|
||||
orderable: false
|
||||
}],
|
||||
language: {
|
||||
emptyTable: 'No reports found',
|
||||
zeroRecords: 'No matching reports found'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderReports(response) {
|
||||
lastJsonResponse = response;
|
||||
|
||||
const reports = response.reports || [];
|
||||
const tbody = $('#reportsTableBody');
|
||||
|
||||
tbody.empty();
|
||||
|
||||
$('#reportCountBadge').text(reports.length);
|
||||
|
||||
if (reports.length > 0) {
|
||||
reports.forEach(function(report) {
|
||||
const reportDateOrder = report.data || '';
|
||||
const printDateOrder = report.data_stampa || '';
|
||||
const statusOrder = report.firmato === true ? 1 : 0;
|
||||
|
||||
const row = `
|
||||
<tr>
|
||||
<td>
|
||||
<strong>${escapeHtml(report.codice_rapporto || '-')}</strong>
|
||||
</td>
|
||||
<td>${escapeHtml(report.id_rapporto || '-')}</td>
|
||||
<td data-order="${escapeHtml(reportDateOrder)}">${escapeHtml(formatDate(report.data))}</td>
|
||||
<td data-order="${escapeHtml(printDateOrder)}">${escapeHtml(formatDate(report.data_stampa))}</td>
|
||||
<td>${escapeHtml(report.versione !== null && report.versione !== undefined ? report.versione : '-')}</td>
|
||||
<td data-order="${statusOrder}">${renderStatus(report.firmato)}</td>
|
||||
<td class="text-center">${renderPdfCell(report.pdf_files)}</td>
|
||||
</tr>
|
||||
`;
|
||||
|
||||
tbody.append(row);
|
||||
});
|
||||
}
|
||||
|
||||
$('#jsonPreview').text(JSON.stringify(response, null, 4));
|
||||
|
||||
$('#emptyState').hide();
|
||||
$('#resultContainer').show();
|
||||
|
||||
setTimeout(function() {
|
||||
initReportsDataTable();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
$('#idCliente').on('change', function() {
|
||||
updateSelectedCustomerBox();
|
||||
resetResults();
|
||||
});
|
||||
|
||||
$('#customerReportsForm').on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const idCliente = $('#idCliente').val();
|
||||
const limit = $('#limitReports').val();
|
||||
const signedStatus = $('#signedStatus').val();
|
||||
|
||||
if (!idCliente) {
|
||||
Swal.fire({
|
||||
title: 'Missing customer',
|
||||
text: 'Please select a customer.',
|
||||
icon: 'warning',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
resetResults();
|
||||
updateSelectedCustomerBox();
|
||||
setLoading(true);
|
||||
|
||||
$.ajax({
|
||||
url: 'get_rapporti_cliente.php',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
id_cliente: idCliente,
|
||||
limit: limit,
|
||||
signed_status: signedStatus
|
||||
},
|
||||
success: function(response) {
|
||||
if (!response || response.success !== true) {
|
||||
Swal.fire({
|
||||
title: 'No data',
|
||||
text: response && response.error ? response.error : 'No reports were returned.',
|
||||
icon: 'warning',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
renderReports(response);
|
||||
},
|
||||
error: function(xhr) {
|
||||
let message = 'Unexpected error while loading reports.';
|
||||
|
||||
if (xhr.responseJSON && xhr.responseJSON.error) {
|
||||
message = xhr.responseJSON.error;
|
||||
} else if (xhr.responseText) {
|
||||
message = xhr.responseText.substring(0, 500);
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'Error',
|
||||
text: message,
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
},
|
||||
complete: function() {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#toggleJsonBtn').on('click', function() {
|
||||
const jsonPreview = $('#jsonPreview');
|
||||
const isVisible = jsonPreview.is(':visible');
|
||||
|
||||
jsonPreview.toggle(!isVisible);
|
||||
|
||||
if (isVisible) {
|
||||
$(this).html('<i class="bx bx-code-alt"></i> Show JSON');
|
||||
} else {
|
||||
$(this).html('<i class="bx bx-hide"></i> Hide JSON');
|
||||
}
|
||||
});
|
||||
|
||||
loadCustomers();
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,580 +0,0 @@
|
||||
<?php include('include/headscript.php'); ?>
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<!-- Required meta tags -->
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
|
||||
<!--favicon-->
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
|
||||
<?php include('cssinclude.php'); ?>
|
||||
|
||||
<title>TRF-Project - Test Report Lookup</title>
|
||||
|
||||
<style>
|
||||
.compact-card .card-body {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.lookup-wrapper {
|
||||
max-width: 1100px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.lookup-title {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.lookup-subtitle {
|
||||
font-size: 13px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.result-section-title {
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
margin-bottom: 10px;
|
||||
color: #344767;
|
||||
}
|
||||
|
||||
.info-box {
|
||||
border: 1px solid #e9ecef;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
background: #fff;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 11px;
|
||||
text-transform: uppercase;
|
||||
color: #6c757d;
|
||||
font-weight: 700;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 14px;
|
||||
color: #212529;
|
||||
font-weight: 600;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.status-pill {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 999px;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.status-pill-success {
|
||||
background: #e8fff1;
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.status-pill-warning {
|
||||
background: #fff3cd;
|
||||
color: #b58100;
|
||||
}
|
||||
|
||||
.pdf-card {
|
||||
border: 1px solid #f1d1d1;
|
||||
background: #fffafa;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.pdf-icon {
|
||||
width: 46px;
|
||||
height: 46px;
|
||||
border-radius: 12px;
|
||||
background: #dc3545;
|
||||
color: #fff;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 24px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.pdf-file-name {
|
||||
font-weight: 700;
|
||||
font-size: 14px;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.pdf-meta {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.json-preview {
|
||||
display: none;
|
||||
background: #111827;
|
||||
color: #e5e7eb;
|
||||
border-radius: 10px;
|
||||
padding: 14px;
|
||||
font-size: 12px;
|
||||
max-height: 420px;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
.empty-state {
|
||||
border: 1px dashed #ced4da;
|
||||
border-radius: 10px;
|
||||
padding: 24px;
|
||||
text-align: center;
|
||||
color: #6c757d;
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.spinner-inline {
|
||||
display: none;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.btn-download-pdf {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.pdf-card {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.btn-download-pdf {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<!--wrapper-->
|
||||
<div class="wrapper">
|
||||
|
||||
<!--sidebar wrapper -->
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<!--end sidebar wrapper -->
|
||||
|
||||
<!--start header -->
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<!--end header -->
|
||||
|
||||
<!--start page wrapper -->
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<?php include('top_stat_widget.php'); ?>
|
||||
|
||||
<div class="lookup-wrapper">
|
||||
|
||||
<div class="card radius-10 compact-card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div>
|
||||
<div class="lookup-title">Test Report Lookup</div>
|
||||
<div class="lookup-subtitle">
|
||||
Search a test report from VisualLims by report number and download the PDF if available.
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<form id="reportSearchForm" class="row g-3 align-items-end">
|
||||
<div class="col-md-8">
|
||||
<label for="codiceRapporto" class="form-label fw-semibold">
|
||||
Report Number
|
||||
</label>
|
||||
<input type="text"
|
||||
class="form-control"
|
||||
id="codiceRapporto"
|
||||
name="codiceRapporto"
|
||||
placeholder="Example: 2621521"
|
||||
autocomplete="off">
|
||||
</div>
|
||||
|
||||
<div class="col-md-4">
|
||||
<button type="submit" id="btnSearchReport" class="btn btn-primary w-100">
|
||||
<i class="bx bx-search"></i> Proceed
|
||||
<span class="spinner-border spinner-border-sm spinner-inline" id="searchSpinner" role="status" aria-hidden="true"></span>
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="resultContainer" style="display:none;">
|
||||
|
||||
<div class="card radius-10 compact-card">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<h6 class="mb-0">Report Data</h6>
|
||||
|
||||
<button type="button" id="toggleJsonBtn" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bx bx-code-alt"></i> Show JSON
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="result-section-title">General Information</div>
|
||||
|
||||
<div class="row g-3 mb-4">
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Report Number</div>
|
||||
<div class="info-value" id="resCodiceRapporto">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Report ID</div>
|
||||
<div class="info-value" id="resIdRapporto">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Customer Code</div>
|
||||
<div class="info-value" id="resCodiceCliente">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Customer Name</div>
|
||||
<div class="info-value" id="resNominativoCliente">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Report Date</div>
|
||||
<div class="info-value" id="resDataRapporto">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Print Date</div>
|
||||
<div class="info-value" id="resDataStampa">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Version</div>
|
||||
<div class="info-value" id="resVersione">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-3">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Signed</div>
|
||||
<div class="info-value" id="resFirmato">-</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<div class="info-box">
|
||||
<div class="info-label">Detail Endpoint</div>
|
||||
<div class="info-value small" id="resDetailEndpoint">-</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="result-section-title">Available PDF Files</div>
|
||||
|
||||
<div id="pdfFilesContainer" class="mb-4"></div>
|
||||
|
||||
<pre id="jsonPreview" class="json-preview"></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div id="emptyState" class="empty-state">
|
||||
<i class="bx bx-file-find" style="font-size:34px;"></i>
|
||||
<div class="mt-2 fw-semibold">No report loaded</div>
|
||||
<div class="small">Enter a report number and click Proceed.</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!--end page wrapper -->
|
||||
|
||||
<!--start overlay-->
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<!--end overlay-->
|
||||
|
||||
<!--Start Back To Top Button-->
|
||||
<a href="javaScript:;" class="back-to-top">
|
||||
<i class='bx bxs-up-arrow-alt'></i>
|
||||
</a>
|
||||
<!--End Back To Top Button-->
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
|
||||
</div>
|
||||
<!--end wrapper-->
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
let lastJsonResponse = null;
|
||||
|
||||
function escapeHtml(value) {
|
||||
if (value === null || value === undefined) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return String(value)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/</g, '<')
|
||||
.replace(/>/g, '>')
|
||||
.replace(/"/g, '"')
|
||||
.replace(/'/g, ''');
|
||||
}
|
||||
|
||||
function formatDate(value) {
|
||||
if (!value) {
|
||||
return '-';
|
||||
}
|
||||
|
||||
const date = new Date(value);
|
||||
|
||||
if (isNaN(date.getTime())) {
|
||||
return value;
|
||||
}
|
||||
|
||||
return date.toLocaleString('it-IT', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
}
|
||||
|
||||
function setLoading(isLoading) {
|
||||
$('#btnSearchReport').prop('disabled', isLoading);
|
||||
$('#searchSpinner').toggle(isLoading);
|
||||
}
|
||||
|
||||
function resetResult() {
|
||||
lastJsonResponse = null;
|
||||
|
||||
$('#resultContainer').hide();
|
||||
$('#emptyState').show();
|
||||
$('#jsonPreview').hide().text('');
|
||||
$('#toggleJsonBtn').html('<i class="bx bx-code-alt"></i> Show JSON');
|
||||
|
||||
$('#resCodiceRapporto').text('-');
|
||||
$('#resIdRapporto').text('-');
|
||||
$('#resCodiceCliente').text('-');
|
||||
$('#resNominativoCliente').text('-');
|
||||
$('#resDataRapporto').text('-');
|
||||
$('#resDataStampa').text('-');
|
||||
$('#resVersione').text('-');
|
||||
$('#resFirmato').text('-');
|
||||
$('#resDetailEndpoint').text('-');
|
||||
$('#pdfFilesContainer').html('');
|
||||
}
|
||||
|
||||
function renderPdfFiles(pdfFiles) {
|
||||
const container = $('#pdfFilesContainer');
|
||||
container.empty();
|
||||
|
||||
if (!Array.isArray(pdfFiles) || pdfFiles.length === 0) {
|
||||
container.html(`
|
||||
<div class="empty-state">
|
||||
<i class="bx bx-file-blank" style="font-size:28px;"></i>
|
||||
<div class="mt-2 fw-semibold">No PDF available</div>
|
||||
<div class="small">No report PDF file was returned by VisualLims.</div>
|
||||
</div>
|
||||
`);
|
||||
return;
|
||||
}
|
||||
|
||||
pdfFiles.forEach(function(file) {
|
||||
const idRapportoFile = file.id_rapporto_file || '';
|
||||
const fileName = file.file_name || 'report.pdf';
|
||||
const categoria = file.categoria || '-';
|
||||
const tipoRapporto = file.tipo_rapporto || '-';
|
||||
|
||||
const downloadUrl = 'download_rapporto_pdf.php?id_rapporto_file=' + encodeURIComponent(idRapportoFile);
|
||||
|
||||
const html = `
|
||||
<div class="pdf-card mb-2">
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<div class="pdf-icon">
|
||||
<i class="bx bxs-file-pdf"></i>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div class="pdf-file-name">${escapeHtml(fileName)}</div>
|
||||
<div class="pdf-meta">
|
||||
ID File: ${escapeHtml(idRapportoFile)}
|
||||
|
|
||||
Category: ${escapeHtml(categoria)}
|
||||
|
|
||||
Type: ${escapeHtml(tipoRapporto)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<a href="${downloadUrl}" target="_blank" class="btn btn-danger btn-sm btn-download-pdf">
|
||||
<i class="bx bx-download"></i> Download PDF
|
||||
</a>
|
||||
</div>
|
||||
`;
|
||||
|
||||
container.append(html);
|
||||
});
|
||||
}
|
||||
|
||||
function renderResult(response) {
|
||||
lastJsonResponse = response;
|
||||
|
||||
const base = response.rapporto_base || {};
|
||||
const data = response.data || {};
|
||||
const cliente = response.cliente || data.Cliente || {};
|
||||
|
||||
$('#resCodiceRapporto').text(response.codice_rapporto || base.CodiceRapporto || '-');
|
||||
$('#resIdRapporto').text(response.id_rapporto || base.IdRapporto || '-');
|
||||
$('#resCodiceCliente').text(cliente.CodiceCliente || '-');
|
||||
$('#resNominativoCliente').text(cliente.Nominativo || '-');
|
||||
$('#resDataRapporto').text(formatDate(base.Data || data.Data));
|
||||
$('#resDataStampa').text(formatDate(base.DataStampa || data.DataStampa));
|
||||
$('#resVersione').text(base.Versione !== undefined ? base.Versione : (data.Versione !== undefined ? data.Versione : '-'));
|
||||
$('#resDetailEndpoint').text(response.detail_endpoint || '-');
|
||||
|
||||
const isSigned = base.Firmato === true || data.Firmato === true;
|
||||
|
||||
if (isSigned) {
|
||||
$('#resFirmato').html('<span class="status-pill status-pill-success"><i class="bx bx-check-circle"></i> Signed</span>');
|
||||
} else {
|
||||
$('#resFirmato').html('<span class="status-pill status-pill-warning"><i class="bx bx-time"></i> Not signed</span>');
|
||||
}
|
||||
|
||||
renderPdfFiles(response.pdf_files || []);
|
||||
|
||||
$('#jsonPreview').text(JSON.stringify(response, null, 4));
|
||||
|
||||
$('#emptyState').hide();
|
||||
$('#resultContainer').show();
|
||||
}
|
||||
|
||||
$('#reportSearchForm').on('submit', function(event) {
|
||||
event.preventDefault();
|
||||
|
||||
const codiceRapporto = $('#codiceRapporto').val().trim();
|
||||
|
||||
if (!codiceRapporto) {
|
||||
Swal.fire({
|
||||
title: 'Missing report number',
|
||||
text: 'Please enter a report number.',
|
||||
icon: 'warning',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
resetResult();
|
||||
setLoading(true);
|
||||
|
||||
$.ajax({
|
||||
url: 'get_rapporto_prova.php',
|
||||
method: 'GET',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
codice: codiceRapporto,
|
||||
step: 'files'
|
||||
},
|
||||
success: function(response) {
|
||||
if (!response || response.success !== true) {
|
||||
Swal.fire({
|
||||
title: 'Report not found',
|
||||
text: response && response.message ? response.message : 'No report was found for this number.',
|
||||
icon: 'warning',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
renderResult(response);
|
||||
},
|
||||
error: function(xhr) {
|
||||
let message = 'Unexpected error while loading the report.';
|
||||
|
||||
if (xhr.responseJSON && xhr.responseJSON.error) {
|
||||
message = xhr.responseJSON.error;
|
||||
} else if (xhr.responseJSON && xhr.responseJSON.message) {
|
||||
message = xhr.responseJSON.message;
|
||||
} else if (xhr.responseText) {
|
||||
message = xhr.responseText.substring(0, 500);
|
||||
}
|
||||
|
||||
Swal.fire({
|
||||
title: 'Error',
|
||||
text: message,
|
||||
icon: 'error',
|
||||
confirmButtonText: 'OK'
|
||||
});
|
||||
},
|
||||
complete: function() {
|
||||
setLoading(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#toggleJsonBtn').on('click', function() {
|
||||
const jsonPreview = $('#jsonPreview');
|
||||
const isVisible = jsonPreview.is(':visible');
|
||||
|
||||
jsonPreview.toggle(!isVisible);
|
||||
|
||||
if (isVisible) {
|
||||
$(this).html('<i class="bx bx-code-alt"></i> Show JSON');
|
||||
} else {
|
||||
$(this).html('<i class="bx bx-hide"></i> Hide JSON');
|
||||
}
|
||||
});
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const prefillCodice = urlParams.get('codice');
|
||||
|
||||
if (prefillCodice) {
|
||||
$('#codiceRapporto').val(prefillCodice);
|
||||
$('#reportSearchForm').trigger('submit');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,385 +0,0 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
|
||||
// Binding preparati da import_insert.php (da risolvere + collegati in automatico).
|
||||
$pending = $_SESSION['pending_bindings'] ?? null;
|
||||
|
||||
if (empty($pending) || (empty($pending['items']) && empty($pending['auto']) && empty($pending['saved']))) {
|
||||
// Niente da mostrare: vado alla griglia.
|
||||
$tid = $pending['template_id'] ?? ($_SESSION['template_id'] ?? null);
|
||||
$ref = $pending['importref'] ?? '';
|
||||
unset($_SESSION['pending_bindings']);
|
||||
|
||||
if ($tid) {
|
||||
header("Location: imported.php?id=" . urlencode($tid) . "&importref=" . urlencode($ref));
|
||||
} else {
|
||||
header("Location: xlstemplates_grid.php");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
$templateId = (int) $pending['template_id'];
|
||||
$importRef = (string) $pending['importref'];
|
||||
$items = $pending['items'] ?? [];
|
||||
$autoItems = $pending['auto'] ?? [];
|
||||
$savedItems = $pending['saved'] ?? [];
|
||||
|
||||
// Righe gia' risolte (modificabili): auto-collegate + binding gia' salvati.
|
||||
$resolvedItems = [];
|
||||
foreach ($autoItems as $a) {
|
||||
$a['badge'] = 'auto';
|
||||
$a['badge_class'] = 'bg-success';
|
||||
$resolvedItems[] = $a;
|
||||
}
|
||||
foreach ($savedItems as $s) {
|
||||
$s['badge'] = 'salvato';
|
||||
$s['badge_class'] = 'bg-secondary';
|
||||
$resolvedItems[] = $s;
|
||||
}
|
||||
|
||||
// Raggruppa le righe per campo (custom: mapping_id, fixed: fixed_field_key),
|
||||
// mantenendo l'ordine di prima apparizione. Ogni gruppo elenca prima i pending.
|
||||
$groups = [];
|
||||
$groupOrder = [];
|
||||
$pushToGroup = function (array $row, string $type, $idx = null) use (&$groups, &$groupOrder) {
|
||||
$kind = $row['kind'] ?? 'custom';
|
||||
$gkey = $kind === 'fixed'
|
||||
? 'fx:' . ($row['fixed_field_key'] ?? '')
|
||||
: 'cf:' . (int) ($row['mapping_id'] ?? 0);
|
||||
if (!isset($groups[$gkey])) {
|
||||
$groups[$gkey] = [
|
||||
'label' => $row['field_label'] ?? $gkey,
|
||||
'kind' => $kind,
|
||||
'pending' => [],
|
||||
'resolved' => [],
|
||||
];
|
||||
$groupOrder[] = $gkey;
|
||||
}
|
||||
if ($type === 'pending') {
|
||||
$groups[$gkey]['pending'][] = ['idx' => $idx, 'data' => $row];
|
||||
} else {
|
||||
$groups[$gkey]['resolved'][] = ['data' => $row];
|
||||
}
|
||||
};
|
||||
foreach ($items as $idx => $item) {
|
||||
$pushToGroup($item, 'pending', $idx);
|
||||
}
|
||||
foreach ($resolvedItems as $res) {
|
||||
$pushToGroup($res, 'resolved');
|
||||
}
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
$stmt = $pdo->prepare("SELECT name FROM excel_templates WHERE id = ?");
|
||||
$stmt->execute([$templateId]);
|
||||
$templateName = $stmt->fetchColumn() ?: ('Template ' . $templateId);
|
||||
?>
|
||||
<!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/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet">
|
||||
<style>
|
||||
.json-value {
|
||||
font-family: Consolas, Monaco, monospace;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.binding-status {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
td .select2-container {
|
||||
min-width: 240px;
|
||||
}
|
||||
|
||||
/* Allinea l'altezza di select2 a form-select/btn di Bootstrap */
|
||||
.select2-container--default .select2-selection--single {
|
||||
height: 38px;
|
||||
border-color: #ced4da;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__rendered {
|
||||
line-height: 36px;
|
||||
}
|
||||
|
||||
.select2-container--default .select2-selection--single .select2-selection__arrow {
|
||||
height: 36px;
|
||||
}
|
||||
</style>
|
||||
<title>Binding JSON → LIMS - <?= 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 radius-10">
|
||||
<div class="card-header">
|
||||
<div class="d-flex align-items-center justify-content-between flex-wrap gap-2">
|
||||
<div>
|
||||
<h6 class="mb-0">Binding JSON → LIMS</h6>
|
||||
<small><?= htmlspecialchars($templateName) ?> · Template ID: <?= $templateId ?></small>
|
||||
</div>
|
||||
<a href="bindings_manage.php?template_id=<?= $templateId ?>" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="fas fa-cog"></i> Gestione binding
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<?php if (!empty($items)): ?>
|
||||
<div class="alert alert-info">
|
||||
Alcuni valori importati dal JSON non hanno ancora una corrispondenza con i valori del LIMS.
|
||||
Seleziona il valore LIMS corretto per ciascuno e conferma. I binding verranno salvati e
|
||||
riutilizzati nelle importazioni successive.
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($autoItems)): ?>
|
||||
<div class="alert alert-success">
|
||||
<?= count($autoItems) ?> valore/i collegato/i automaticamente (corrispondenza esatta con il LIMS).
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div id="bindingError" class="alert alert-danger" style="display:none;"></div>
|
||||
|
||||
<form id="bindingForm">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered align-middle">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width:32px;"></th>
|
||||
<th>Valore JSON</th>
|
||||
<th>Valore LIMS</th>
|
||||
<th style="width:210px;">Azioni</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php foreach ($groupOrder as $gkey): $g = $groups[$gkey];
|
||||
$pendCount = count($g['pending']);
|
||||
$totalCount = $pendCount + count($g['resolved']); ?>
|
||||
<tr class="binding-group-header table-light">
|
||||
<td colspan="4">
|
||||
<strong><?= htmlspecialchars($g['label']) ?></strong>
|
||||
<?php if ($g['kind'] === 'fixed'): ?><span class="badge bg-light text-dark border">fixed</span><?php endif; ?>
|
||||
<span class="text-muted">· <?= $totalCount ?> valore/i<?php if ($pendCount): ?>, <?= $pendCount ?> da risolvere<?php endif; ?></span>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<?php foreach ($g['pending'] as $p): $idx = $p['idx']; $item = $p['data']; $kind = $item['kind'] ?? 'custom'; ?>
|
||||
<tr class="binding-row"
|
||||
data-index="<?= $idx ?>"
|
||||
data-kind="<?= $kind ?>"
|
||||
<?php if ($kind === 'fixed'): ?>
|
||||
data-fixed-key="<?= htmlspecialchars($item['fixed_field_key'], ENT_QUOTES) ?>"
|
||||
<?php else: ?>
|
||||
data-mapping-id="<?= (int) ($item['mapping_id'] ?? 0) ?>"
|
||||
data-field-id="<?= (int) ($item['field_id'] ?? 0) ?>"
|
||||
<?php endif; ?>
|
||||
data-json-value="<?= htmlspecialchars($item['json_value'], ENT_QUOTES) ?>"
|
||||
data-datadb-ids="<?= htmlspecialchars(json_encode($item['datadb_ids']), ENT_QUOTES) ?>">
|
||||
<td class="text-muted ps-4">›</td>
|
||||
<td class="json-value"><?= htmlspecialchars($item['json_value']) ?></td>
|
||||
<td>
|
||||
<select class="form-select binding-select">
|
||||
<option value="">Seleziona valore LIMS...</option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary skip-binding-btn">Nessuna corrispondenza</button>
|
||||
<div class="binding-status text-muted mt-1">In attesa</div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php foreach ($g['resolved'] as $r): $res = $r['data']; $kind = $res['kind'] ?? 'custom'; ?>
|
||||
<tr class="binding-row"
|
||||
data-kind="<?= $kind ?>"
|
||||
<?php if ($kind === 'fixed'): ?>
|
||||
data-fixed-key="<?= htmlspecialchars($res['fixed_field_key'], ENT_QUOTES) ?>"
|
||||
<?php else: ?>
|
||||
data-mapping-id="<?= (int) ($res['mapping_id'] ?? 0) ?>"
|
||||
data-field-id="<?= (int) ($res['field_id'] ?? 0) ?>"
|
||||
<?php endif; ?>
|
||||
data-json-value="<?= htmlspecialchars($res['json_value'], ENT_QUOTES) ?>"
|
||||
data-datadb-ids="<?= htmlspecialchars(json_encode($res['datadb_ids']), ENT_QUOTES) ?>">
|
||||
<td class="text-muted ps-4">›</td>
|
||||
<td class="json-value"><?= htmlspecialchars($res['json_value']) ?></td>
|
||||
<td>
|
||||
<select class="form-select binding-select">
|
||||
<option value="<?= (int) $res['lims_value_id'] ?>" selected><?= htmlspecialchars($res['lims_value']) ?></option>
|
||||
</select>
|
||||
</td>
|
||||
<td>
|
||||
<button type="button" class="btn btn-sm btn-outline-secondary skip-binding-btn">Nessuna corrispondenza</button>
|
||||
<div class="binding-status mt-1"><span class="badge <?= $res['badge_class'] ?>"><?= $res['badge'] ?></span></div>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; ?>
|
||||
<?php endforeach; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2 mt-3">
|
||||
<a href="imported.php?id=<?= $templateId ?>&importref=<?= urlencode($importRef) ?>"
|
||||
class="btn btn-outline-secondary">Salta per ora</a>
|
||||
<button type="submit" class="btn btn-primary" id="confirmBindingsBtn" disabled>
|
||||
Conferma e prosegui
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</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>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
$(function() {
|
||||
const TEMPLATE_ID = <?= $templateId ?>;
|
||||
const IMPORT_REF = <?= json_encode($importRef) ?>;
|
||||
const CONTINUE_URL = 'imported.php?id=' + TEMPLATE_ID + '&importref=' + encodeURIComponent(IMPORT_REF);
|
||||
|
||||
const $form = $('#bindingForm');
|
||||
const $btn = $('#confirmBindingsBtn');
|
||||
const $error = $('#bindingError');
|
||||
|
||||
// Dropdown valori LIMS per riga: sorgente custom vs fixed.
|
||||
$('.binding-select').each(function() {
|
||||
const $row = $(this).closest('.binding-row');
|
||||
const kind = $row.data('kind');
|
||||
const isFixed = kind === 'fixed';
|
||||
|
||||
$(this).select2({
|
||||
placeholder: 'Seleziona valore LIMS...',
|
||||
width: '100%',
|
||||
ajax: {
|
||||
url: isFixed ? 'search_fixed_field_values.php' : 'search_customfield_values.php',
|
||||
dataType: 'json',
|
||||
delay: 200,
|
||||
data: params => isFixed ? {
|
||||
field_key: $row.data('fixed-key'),
|
||||
template_id: TEMPLATE_ID,
|
||||
q: params.term || '',
|
||||
limit: 50
|
||||
} : {
|
||||
field_id: $row.data('field-id'),
|
||||
q: params.term || '',
|
||||
limit: 50
|
||||
},
|
||||
processResults: data => ({
|
||||
results: data.results || []
|
||||
})
|
||||
},
|
||||
minimumInputLength: 0
|
||||
});
|
||||
});
|
||||
|
||||
// Una riga e' pronta se ha un valore scelto oppure e' marcata "nessuna corrispondenza".
|
||||
function refreshButton() {
|
||||
const allReady = $('.binding-row').toArray().every(row => {
|
||||
const $row = $(row);
|
||||
return $row.hasClass('is-skipped') || $row.find('.binding-select').val();
|
||||
});
|
||||
$btn.prop('disabled', !allReady);
|
||||
}
|
||||
|
||||
$('.binding-select').on('change', refreshButton);
|
||||
refreshButton();
|
||||
|
||||
// "Nessuna corrispondenza": azzera il valore importato, nessun binding salvato.
|
||||
$('.skip-binding-btn').on('click', function() {
|
||||
const $row = $(this).closest('.binding-row');
|
||||
const $select = $row.find('.binding-select');
|
||||
const $status = $row.find('.binding-status');
|
||||
const skipped = $row.toggleClass('is-skipped').hasClass('is-skipped');
|
||||
|
||||
$(this).toggleClass('btn-outline-secondary', !skipped).toggleClass('btn-secondary', skipped);
|
||||
$select.val(null).trigger('change').prop('disabled', skipped);
|
||||
$status.text(skipped ? 'Nessuna corrispondenza' : 'In attesa');
|
||||
refreshButton();
|
||||
});
|
||||
|
||||
$form.on('submit', function(e) {
|
||||
e.preventDefault();
|
||||
$error.hide();
|
||||
$btn.prop('disabled', true).text('Salvataggio...');
|
||||
|
||||
const tasks = $('.binding-row').toArray().map(row => {
|
||||
const $row = $(row);
|
||||
const $status = $row.find('.binding-status');
|
||||
const kind = $row.data('kind');
|
||||
const isFixed = kind === 'fixed';
|
||||
const datadbIds = JSON.stringify($row.data('datadb-ids') || []);
|
||||
const jsonValue = String($row.data('json-value'));
|
||||
|
||||
const targetFields = isFixed
|
||||
? { kind: 'fixed', fixed_field_key: $row.data('fixed-key') }
|
||||
: { kind: 'custom', mapping_id: $row.data('mapping-id'), field_id: $row.data('field-id') };
|
||||
|
||||
// Riga senza corrispondenza: azzera il valore, niente binding.
|
||||
if ($row.hasClass('is-skipped')) {
|
||||
return $.post('skip_binding.php', {
|
||||
...targetFields,
|
||||
template_id: TEMPLATE_ID,
|
||||
json_value: jsonValue,
|
||||
datadb_ids: datadbIds
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$status.text('Azzerato').removeClass('text-muted').addClass('text-success');
|
||||
return true;
|
||||
}
|
||||
$status.text('Errore').addClass('text-danger');
|
||||
throw new Error((resp && resp.error) || 'Errore azzeramento valore');
|
||||
});
|
||||
}
|
||||
|
||||
const $select = $row.find('.binding-select');
|
||||
const selectedData = $select.select2('data')[0] || {};
|
||||
|
||||
return $.post('save_binding.php', {
|
||||
...targetFields,
|
||||
template_id: TEMPLATE_ID,
|
||||
json_value: jsonValue,
|
||||
lims_value_id: $select.val(),
|
||||
lims_value: selectedData.text || '',
|
||||
datadb_ids: datadbIds
|
||||
}).then(resp => {
|
||||
if (resp && resp.success) {
|
||||
$status.text('Salvato').removeClass('text-muted').addClass('text-success');
|
||||
return true;
|
||||
}
|
||||
$status.text('Errore').addClass('text-danger');
|
||||
throw new Error((resp && resp.error) || 'Errore salvataggio binding');
|
||||
});
|
||||
});
|
||||
|
||||
Promise.all(tasks)
|
||||
.then(() => {
|
||||
window.location.href = CONTINUE_URL;
|
||||
})
|
||||
.catch(err => {
|
||||
$error.text(err.message || 'Errore durante il salvataggio dei binding.').show();
|
||||
$btn.prop('disabled', false).text('Conferma e prosegui');
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
ini_set('log_errors', 1);
|
||||
ini_set('error_log', __DIR__ . '/routine_debug.log');
|
||||
|
||||
function applyRoutine(&$excelData, $routineData)
|
||||
{
|
||||
try {
|
||||
// Initial log
|
||||
error_log("Inizio esecuzione routine Le Silla: " . date('Y-m-d H:i:s'));
|
||||
error_log("Dati routine: " . print_r($routineData, true));
|
||||
error_log("Dati excel_data: " . print_r($excelData, true));
|
||||
|
||||
// Check if excelData is empty
|
||||
if (empty($excelData)) {
|
||||
throw new Exception("excelData è vuoto o non valido.");
|
||||
}
|
||||
|
||||
// Extract routine settings with default values
|
||||
$articolo_header = trim($routineData['articolo'] ?? 'ARTICOLO');
|
||||
$descrizione_header = trim($routineData['descrizione'] ?? 'DESCRIZIONE');
|
||||
$headers = $routineData['xls_headers'] ?? [];
|
||||
|
||||
if (empty($headers)) {
|
||||
throw new Exception("Nessun header trovato per la routine Le Silla.");
|
||||
}
|
||||
|
||||
// If headers arrive as JSON string, decode them
|
||||
if (!is_array($headers) && is_string($headers)) {
|
||||
$decoded_headers = json_decode($headers, true);
|
||||
if (json_last_error() === JSON_ERROR_NONE && is_array($decoded_headers)) {
|
||||
$headers = $decoded_headers;
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_array($headers) || empty($headers)) {
|
||||
throw new Exception("Gli header della routine non sono validi.");
|
||||
}
|
||||
|
||||
error_log("Header ricevuti: " . print_r($headers, true));
|
||||
|
||||
// Normalize headers
|
||||
$normalized_headers = array_map(function ($header) {
|
||||
return trim((string)$header);
|
||||
}, $headers);
|
||||
|
||||
error_log("Header normalizzati: " . print_r($normalized_headers, true));
|
||||
error_log("Header values - ARTICOLO: '$articolo_header', DESCRIZIONE: '$descrizione_header'");
|
||||
|
||||
// Find column indexes
|
||||
$articolo_index = array_search($articolo_header, $normalized_headers, true);
|
||||
$descrizione_index = array_search($descrizione_header, $normalized_headers, true);
|
||||
|
||||
if ($articolo_index === false) {
|
||||
throw new Exception("Colonna ARTICOLO non trovata.");
|
||||
}
|
||||
if ($descrizione_index === false) {
|
||||
throw new Exception("Colonna DESCRIZIONE non trovata.");
|
||||
}
|
||||
|
||||
error_log("Indici colonne - ARTICOLO: $articolo_index, DESCRIZIONE: $descrizione_index");
|
||||
|
||||
/**
|
||||
* Merge ARTICOLO + DESCRIZIONE into ARTICOLO separated by " - "
|
||||
*/
|
||||
foreach ($excelData as $index => $row) {
|
||||
if (!isset($row['data']) || !is_array($row['data'])) {
|
||||
error_log("Riga non valida, manca 'data' all'indice $index");
|
||||
continue;
|
||||
}
|
||||
|
||||
$value_articolo = trim((string)($row['data'][$articolo_index] ?? ''));
|
||||
$value_descrizione = trim((string)($row['data'][$descrizione_index] ?? ''));
|
||||
|
||||
$parts = [];
|
||||
if ($value_articolo !== '') {
|
||||
$parts[] = $value_articolo;
|
||||
}
|
||||
if ($value_descrizione !== '') {
|
||||
$parts[] = $value_descrizione;
|
||||
}
|
||||
|
||||
$excelData[$index]['data'][$articolo_index] = implode(' - ', $parts);
|
||||
}
|
||||
|
||||
error_log("Routine Le Silla completata con successo - Merge applicato su " . count($excelData) . " righe");
|
||||
} catch (Exception $e) {
|
||||
error_log("Eccezione nella routine Le Silla: " . $e->getMessage());
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Salva un binding JSON -> LIMS (custom o fixed) e lo applica ai record appena importati. Ritorna JSON.
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$kind = ($_POST['kind'] ?? 'custom') === 'fixed' ? 'fixed' : 'custom';
|
||||
$templateId = intval($_POST['template_id'] ?? 0);
|
||||
$jsonValue = (string) ($_POST['json_value'] ?? '');
|
||||
$limsValueId = intval($_POST['lims_value_id'] ?? 0);
|
||||
$limsValue = (string) ($_POST['lims_value'] ?? '');
|
||||
|
||||
$datadbIds = [];
|
||||
if (isset($_POST['datadb_ids'])) {
|
||||
$decoded = json_decode($_POST['datadb_ids'], true);
|
||||
if (is_array($decoded)) {
|
||||
$datadbIds = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if ($templateId <= 0 || $jsonValue === '' || $limsValueId <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing required parameters']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
$createdBy = Auth::user() ? (int) Auth::user()->present()->id : null;
|
||||
|
||||
if ($kind === 'fixed') {
|
||||
$fixedKey = trim($_POST['fixed_field_key'] ?? '');
|
||||
$column = binding_fixed_column($fixedKey);
|
||||
if ($fixedKey === '' || !binding_fixed_is_list($fixedKey) || !$column) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid fixed field']);
|
||||
exit;
|
||||
}
|
||||
|
||||
binding_upsert_fixed($pdo, $templateId, $fixedKey, $jsonValue, $limsValueId, $limsValue, $createdBy);
|
||||
$applied = !empty($datadbIds) ? binding_apply_to_datadb($pdo, $column, $limsValueId, $datadbIds) : 0;
|
||||
} else {
|
||||
$mappingId = intval($_POST['mapping_id'] ?? 0);
|
||||
$fieldId = intval($_POST['field_id'] ?? 0);
|
||||
if ($mappingId <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing mapping_id']);
|
||||
exit;
|
||||
}
|
||||
if ($fieldId <= 0) {
|
||||
$stmt = $pdo->prepare("SELECT field_id FROM template_mapping WHERE id = ?");
|
||||
$stmt->execute([$mappingId]);
|
||||
$fieldId = (int) ($stmt->fetchColumn() ?: 0);
|
||||
}
|
||||
|
||||
binding_upsert($pdo, $templateId, $mappingId, $fieldId, $jsonValue, $limsValueId, $limsValue, $createdBy);
|
||||
$applied = !empty($datadbIds) ? binding_apply_to_details($pdo, $mappingId, $limsValue, $datadbIds) : 0;
|
||||
}
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'applied_rows' => $applied,
|
||||
'kind' => $kind,
|
||||
'json_value' => $jsonValue,
|
||||
'lims_value' => $limsValue,
|
||||
'lims_value_id' => $limsValueId,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -815,7 +815,7 @@
|
||||
"IdSchemaCustomFields": 191,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "KIABI",
|
||||
"Descrizione": "Schema per il cliente Kiabi\r\n"
|
||||
"Descrizione": "Schema per il cliente Kiabi\r\n\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 192,
|
||||
@@ -900,6 +900,18 @@
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "Chanel Flammability",
|
||||
"Descrizione": "Schema per Chanel Flammability\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 206,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "COLDWATER CREEK",
|
||||
"Descrizione": "\r\n"
|
||||
},
|
||||
{
|
||||
"IdSchemaCustomFields": 208,
|
||||
"ConteggioClienti": 0,
|
||||
"Nome": "ETON",
|
||||
"Descrizione": "Schema da usare per ETON\r\n"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -19,6 +19,14 @@ $q = mb_strtolower(trim($_GET['q'] ?? ''));
|
||||
$limit = max(1, min(50, intval($_GET['limit'] ?? 20)));
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||
|
||||
$tipo = $_GET['tipo'] ?? ''; // 'attivo' | 'inattivo' | '' (nessun filtro)
|
||||
|
||||
function isInattivo(array $c): bool
|
||||
{
|
||||
$v = $c['Inattivo'] ?? false;
|
||||
return $v === true || $v === 1 || $v === '1' || $v === 'true';
|
||||
}
|
||||
|
||||
function formatClientLabel(array $client): string
|
||||
{
|
||||
$name = trim($client['Nominativo'] ?? '');
|
||||
@@ -36,7 +44,7 @@ function formatClientLabel(array $client): string
|
||||
$suffix = '--';
|
||||
}
|
||||
|
||||
return $name . ' - ' . $suffix . ' (ID: ' . $id . ')';
|
||||
return $name . ' - ' . $suffix . ' - ' . $id;
|
||||
}
|
||||
|
||||
try {
|
||||
@@ -49,7 +57,7 @@ try {
|
||||
$api = VisualLimsApiClient::getInstance();
|
||||
|
||||
$params = [
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente',
|
||||
'$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo',
|
||||
'$orderby' => 'Nominativo asc'
|
||||
];
|
||||
|
||||
@@ -64,6 +72,14 @@ try {
|
||||
|
||||
$clients = $data['value'] ?? [];
|
||||
|
||||
// Filtro tipo, applicato SOLO alla ricerca, non al lookup per id
|
||||
$searchClients = $clients;
|
||||
if ($tipo === 'attivo') {
|
||||
$searchClients = array_values(array_filter($clients, fn($c) => !isInattivo($c)));
|
||||
} elseif ($tipo === 'inattivo') {
|
||||
$searchClients = array_values(array_filter($clients, fn($c) => isInattivo($c)));
|
||||
}
|
||||
|
||||
// If requesting by specific ID, used for loading selected value
|
||||
if ($id !== null) {
|
||||
foreach ($clients as $c) {
|
||||
@@ -88,18 +104,20 @@ try {
|
||||
// Search by query
|
||||
$results = [];
|
||||
|
||||
foreach ($clients as $c) {
|
||||
foreach ($searchClients as $c) {
|
||||
$name = trim($c['Nominativo'] ?? '');
|
||||
$code = trim($c['CodiceCliente'] ?? '');
|
||||
|
||||
// Cerca solo su ciò che viene visualizzato: Nome - Lettera - ID
|
||||
$label = formatClientLabel($c);
|
||||
|
||||
if (
|
||||
$q === '' ||
|
||||
mb_strpos(mb_strtolower($name), $q) !== false ||
|
||||
mb_strpos(mb_strtolower($code), $q) !== false
|
||||
mb_strpos(mb_strtolower($label), $q) !== false
|
||||
) {
|
||||
$results[] = [
|
||||
'id' => $c['IdCliente'],
|
||||
'text' => formatClientLabel($c),
|
||||
'text' => $label,
|
||||
'IdCliente' => $c['IdCliente'],
|
||||
'Nominativo' => $name,
|
||||
'CodiceCliente' => $code
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<?php
|
||||
|
||||
// Select2 source of LIMS values for a fixed field. Returns {results:[{id,text}]}.
|
||||
// Params: field_key (required), template_id (required, drives client-dependent lists),
|
||||
// q (search), id (resolve a single value for preselect), limit.
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$fieldKey = trim($_GET['field_key'] ?? '');
|
||||
$templateId = intval($_GET['template_id'] ?? 0);
|
||||
$q = mb_strtolower(trim($_GET['q'] ?? ''));
|
||||
$id = isset($_GET['id']) ? intval($_GET['id']) : null;
|
||||
$rawLimit = intval($_GET['limit'] ?? 30);
|
||||
$limit = $rawLimit <= 0 ? 0 : max(1, min(500, $rawLimit));
|
||||
|
||||
if ($fieldKey === '' || !binding_fixed_is_list($fieldKey)) {
|
||||
echo json_encode(['results' => []]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
$values = binding_get_fixed_values($pdo, $fieldKey, $templateId);
|
||||
|
||||
if ($id !== null) {
|
||||
foreach ($values as $v) {
|
||||
if ((int) $v['id'] === $id) {
|
||||
echo json_encode(['results' => [['id' => $v['id'], 'text' => $v['text']]]]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
echo json_encode(['results' => []]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$results = [];
|
||||
foreach ($values as $v) {
|
||||
$text = (string) $v['text'];
|
||||
if ($q === '' || mb_strpos(mb_strtolower($text), $q) !== false) {
|
||||
$results[] = ['id' => $v['id'], 'text' => $text];
|
||||
if ($limit > 0 && count($results) >= $limit) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
usort($results, fn($a, $b) => strcasecmp($a['text'], $b['text']));
|
||||
echo json_encode(['results' => $results]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -2,7 +2,11 @@
|
||||
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; }
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
@@ -59,15 +63,66 @@ if (isset($_GET['macro_list'])) {
|
||||
exit;
|
||||
}
|
||||
|
||||
// Search (with optional MacroMatrice filter)
|
||||
$results = [];
|
||||
// Search (with optional MacroMatrice filter) - multi-term AND, position-independent, ranked
|
||||
$terms = array_values(array_filter(
|
||||
preg_split('/\s+/u', $q, -1, PREG_SPLIT_NO_EMPTY)
|
||||
));
|
||||
|
||||
// Nessun termine digitato: comportamento invariato (alfabetico, taglio al limite)
|
||||
if (empty($terms)) {
|
||||
$results = [];
|
||||
foreach ($matrici as $m) {
|
||||
if ($macro !== '' && ($m['MacroMatrice'] ?? '') !== $macro) continue;
|
||||
$results[] = ['id' => $m['IdMatrice'], 'text' => $m['NomeMatrice'] ?? ''];
|
||||
if (count($results) >= $limit) break;
|
||||
}
|
||||
echo json_encode(['results' => $results]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$firstTerm = $terms[0];
|
||||
$scored = [];
|
||||
|
||||
foreach ($matrici as $m) {
|
||||
$nome = $m['NomeMatrice'] ?? '';
|
||||
if ($macro !== '' && ($m['MacroMatrice'] ?? '') !== $macro) continue;
|
||||
if ($q === '' || mb_strpos(mb_strtolower($nome), $q) !== false) {
|
||||
$results[] = ['id' => $m['IdMatrice'], 'text' => $nome];
|
||||
if (count($results) >= $limit) break;
|
||||
|
||||
$nomeLower = mb_strtolower($nome);
|
||||
|
||||
// AND: tutti i termini devono essere presenti (in qualsiasi posizione)
|
||||
$matchAll = true;
|
||||
foreach ($terms as $t) {
|
||||
if (mb_strpos($nomeLower, $t) === false) {
|
||||
$matchAll = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!$matchAll) continue;
|
||||
|
||||
// Punteggio di pertinenza (più basso = più rilevante, per ordinamento crescente)
|
||||
if ($nomeLower === $q) {
|
||||
$score = 0; // match esatto
|
||||
} elseif (mb_strpos($nomeLower, $firstTerm) === 0) {
|
||||
$score = 1; // il nome inizia col primo termine
|
||||
} elseif (preg_match('/(^|\s)' . preg_quote($firstTerm, '/') . '/u', $nomeLower)) {
|
||||
$score = 2; // primo termine a inizio di una parola
|
||||
} else {
|
||||
$score = 3; // termine presente ma non a inizio parola
|
||||
}
|
||||
|
||||
$scored[] = ['id' => $m['IdMatrice'], 'text' => $nome, '_s' => $score, '_n' => $nomeLower];
|
||||
}
|
||||
|
||||
// Ordina per punteggio, poi alfabetico (stabile e prevedibile)
|
||||
usort($scored, function ($a, $b) {
|
||||
if ($a['_s'] !== $b['_s']) return $a['_s'] - $b['_s'];
|
||||
return strcmp($a['_n'], $b['_n']);
|
||||
});
|
||||
|
||||
// Taglia al limite DOPO l'ordinamento e rimuovi i campi interni
|
||||
$results = array_map(
|
||||
fn($r) => ['id' => $r['id'], 'text' => $r['text']],
|
||||
array_slice($scored, 0, $limit)
|
||||
);
|
||||
|
||||
echo json_encode(['results' => $results]);
|
||||
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php
|
||||
|
||||
// "Nessuna corrispondenza": azzera il valore importato (custom o fixed) e rimuove il binding. Ritorna JSON.
|
||||
|
||||
require_once dirname(__DIR__, 2) . '/vendor/autoload.php';
|
||||
require_once __DIR__ . '/class/db-functions.php';
|
||||
require_once __DIR__ . '/class/binding-functions.php';
|
||||
include dirname(__DIR__) . '/../extra/auth.php';
|
||||
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', '0');
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (!Auth::check()) {
|
||||
http_response_code(401);
|
||||
echo json_encode(['success' => false, 'error' => 'Unauthorized']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
|
||||
http_response_code(405);
|
||||
echo json_encode(['success' => false, 'error' => 'Method not allowed']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$kind = ($_POST['kind'] ?? 'custom') === 'fixed' ? 'fixed' : 'custom';
|
||||
$templateId = intval($_POST['template_id'] ?? 0);
|
||||
$jsonValue = (string) ($_POST['json_value'] ?? '');
|
||||
|
||||
$datadbIds = [];
|
||||
if (isset($_POST['datadb_ids'])) {
|
||||
$decoded = json_decode($_POST['datadb_ids'], true);
|
||||
if (is_array($decoded)) {
|
||||
$datadbIds = $decoded;
|
||||
}
|
||||
}
|
||||
|
||||
if ($jsonValue === '') {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing json_value']);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
$pdo = DBHandlerSelect::getInstance()->getConnection();
|
||||
|
||||
if ($kind === 'fixed') {
|
||||
$fixedKey = trim($_POST['fixed_field_key'] ?? '');
|
||||
$column = binding_fixed_column($fixedKey);
|
||||
if ($fixedKey === '' || !binding_fixed_is_list($fixedKey) || !$column || $templateId <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Invalid fixed field']);
|
||||
exit;
|
||||
}
|
||||
$cleared = !empty($datadbIds) ? binding_apply_to_datadb($pdo, $column, null, $datadbIds) : 0;
|
||||
binding_delete_target($pdo, binding_target_fixed($templateId, $fixedKey), $jsonValue);
|
||||
} else {
|
||||
$mappingId = intval($_POST['mapping_id'] ?? 0);
|
||||
if ($mappingId <= 0) {
|
||||
http_response_code(422);
|
||||
echo json_encode(['success' => false, 'error' => 'Missing mapping_id']);
|
||||
exit;
|
||||
}
|
||||
$cleared = !empty($datadbIds) ? binding_apply_to_details($pdo, $mappingId, '', $datadbIds) : 0;
|
||||
binding_delete_target($pdo, binding_target_custom($mappingId), $jsonValue);
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'cleared_rows' => $cleared]);
|
||||
} catch (Exception $e) {
|
||||
http_response_code(500);
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,481 @@
|
||||
<?php include('include/headscript.php'); ?>
|
||||
<!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'); ?>
|
||||
<title>TRF-Project - Statistiche Export LIMS</title>
|
||||
<link rel="stylesheet" href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css">
|
||||
<style>
|
||||
.stat-card .card-body {
|
||||
padding: 1rem 1.25rem;
|
||||
}
|
||||
|
||||
.stat-value {
|
||||
font-size: 1.75rem;
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
}
|
||||
|
||||
.stat-label {
|
||||
font-size: 0.8rem;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .03em;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.chart-wrap {
|
||||
position: relative;
|
||||
height: 420px;
|
||||
}
|
||||
|
||||
#tblClienti,
|
||||
#tblUtenti {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#tblClienti th,
|
||||
#tblClienti td,
|
||||
#tblUtenti th,
|
||||
#tblUtenti td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.filter-bar {
|
||||
gap: .75rem;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="wrapper">
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<?php include('include/topbar.php'); ?>
|
||||
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
|
||||
<!-- ===== FILTRO GIORNATA ===== -->
|
||||
<div class="card radius-10">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center flex-wrap filter-bar">
|
||||
<h6 class="mb-0 me-3"><i class="bx bx-bar-chart-alt-2"></i> Statistiche Export LIMS</h6>
|
||||
<div class="d-flex align-items-center">
|
||||
<label for="filterDate" class="me-2 mb-0 fw-semibold">Giornata:</label>
|
||||
<input type="date" id="filterDate" class="form-control form-control-sm" style="width:auto;">
|
||||
</div>
|
||||
<div class="btn-group btn-group-sm ms-2" role="group">
|
||||
<button class="btn btn-outline-secondary" id="btnToday">Oggi</button>
|
||||
<button class="btn btn-outline-secondary" id="btnYesterday">Ieri</button>
|
||||
</div>
|
||||
<button class="btn btn-primary btn-sm ms-auto" id="btnRefresh">
|
||||
<i class="bx bx-refresh"></i> Aggiorna
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== KPI ===== -->
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-3 col-md-6">
|
||||
<div class="card radius-10 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="stat-label">Export (status L)</div>
|
||||
<div class="stat-value text-primary" id="kpiExport">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-3 col-md-6">
|
||||
<div class="card radius-10 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="stat-label">Campioni inseriti LIMS</div>
|
||||
<div class="stat-value text-info" id="kpiLimsSamples">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-3 col-md-6">
|
||||
<div class="card radius-10 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="stat-label">% Esportati / inseriti</div>
|
||||
<div class="stat-value text-success" id="kpiPerc">—</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-3 col-md-6">
|
||||
<div class="card radius-10 stat-card">
|
||||
<div class="card-body">
|
||||
<div class="stat-label">Clienti distinti</div>
|
||||
<div class="stat-value text-warning" id="kpiClienti">0</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== DATO GIORNALIERO CAMPIONI LIMS ===== -->
|
||||
<div class="card radius-10">
|
||||
<div class="card-body">
|
||||
<div class="d-flex align-items-center flex-wrap" style="gap:.75rem;">
|
||||
<span class="fw-semibold">Campioni totali inseriti nel LIMS per questa giornata:</span>
|
||||
<input type="number" min="0" id="limsSamplesInput" class="form-control form-control-sm" style="width:140px;" placeholder="0">
|
||||
<button class="btn btn-success btn-sm" id="btnSaveLimsSamples">
|
||||
<i class="bx bx-save"></i> Salva dato
|
||||
</button>
|
||||
<small class="text-muted">Un valore al giorno. Usato per calcolare la % di export.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== GRAFICO TORTA ESPORTATI vs TOTALE LIMS ===== -->
|
||||
<div class="row" id="pieRow" style="display:none;">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Esportati vs totale inserito nel LIMS</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-wrap" style="height:340px;"><canvas id="chartPie"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== GRAFICI ===== -->
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Export per cliente (idclient) <small class="text-muted">— top 15</small></h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-wrap"><canvas id="chartClienti"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Export per utente (user_id) <small class="text-muted">— top 15</small></h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="chart-wrap"><canvas id="chartUtenti"></canvas></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ===== LISTE ===== -->
|
||||
<div class="row">
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Dettaglio per cliente</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="tblClienti" class="table table-striped table-bordered table-sm w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID Cliente</th>
|
||||
<th>Cliente</th>
|
||||
<th>Export</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-12 col-lg-6">
|
||||
<div class="card radius-10">
|
||||
<div class="card-header">
|
||||
<h6 class="mb-0">Dettaglio per utente</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table id="tblUtenti" class="table table-striped table-bordered table-sm w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID Utente</th>
|
||||
<th>Utente</th>
|
||||
<th>Export</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</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>
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1/dist/chart.umd.min.js"></script>
|
||||
|
||||
<script>
|
||||
let chartClienti = null;
|
||||
let chartUtenti = null;
|
||||
let chartPie = null;
|
||||
let dtClienti = null;
|
||||
let dtUtenti = null;
|
||||
|
||||
function todayStr() {
|
||||
const d = new Date();
|
||||
const off = d.getTimezoneOffset();
|
||||
return new Date(d.getTime() - off * 60000).toISOString().slice(0, 10);
|
||||
}
|
||||
|
||||
function initTables() {
|
||||
dtClienti = $('#tblClienti').DataTable({
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false,
|
||||
order: [
|
||||
[2, 'desc']
|
||||
],
|
||||
columns: [{
|
||||
data: 'idclient'
|
||||
},
|
||||
{
|
||||
data: 'clientname',
|
||||
defaultContent: '—'
|
||||
},
|
||||
{
|
||||
data: 'tot',
|
||||
className: 'text-end'
|
||||
}
|
||||
],
|
||||
language: {
|
||||
emptyTable: 'Nessun export in questa giornata'
|
||||
}
|
||||
});
|
||||
dtUtenti = $('#tblUtenti').DataTable({
|
||||
paging: false,
|
||||
searching: false,
|
||||
info: false,
|
||||
order: [
|
||||
[2, 'desc']
|
||||
],
|
||||
columns: [{
|
||||
data: 'user_id'
|
||||
},
|
||||
{
|
||||
data: 'username',
|
||||
defaultContent: '—'
|
||||
},
|
||||
{
|
||||
data: 'tot',
|
||||
className: 'text-end'
|
||||
}
|
||||
],
|
||||
language: {
|
||||
emptyTable: 'Nessun export in questa giornata'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function renderBarChart(existing, canvasId, labels, values, label) {
|
||||
if (existing) existing.destroy();
|
||||
const ctx = document.getElementById(canvasId).getContext('2d');
|
||||
return new Chart(ctx, {
|
||||
type: 'bar',
|
||||
data: {
|
||||
labels: labels,
|
||||
datasets: [{
|
||||
label: label,
|
||||
data: values,
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
indexAxis: 'y',
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
display: false
|
||||
}
|
||||
},
|
||||
scales: {
|
||||
x: {
|
||||
beginAtZero: true,
|
||||
ticks: {
|
||||
precision: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Torta: esportati vs non esportati (totale LIMS - esportati)
|
||||
function renderPie(exported, limsTotal) {
|
||||
const pieRow = document.getElementById('pieRow');
|
||||
// Mostra solo se il totale è inserito e valido
|
||||
if (!limsTotal || limsTotal <= 0) {
|
||||
pieRow.style.display = 'none';
|
||||
if (chartPie) {
|
||||
chartPie.destroy();
|
||||
chartPie = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
pieRow.style.display = '';
|
||||
|
||||
const notExported = Math.max(0, limsTotal - exported);
|
||||
if (chartPie) chartPie.destroy();
|
||||
const ctx = document.getElementById('chartPie').getContext('2d');
|
||||
chartPie = new Chart(ctx, {
|
||||
type: 'pie',
|
||||
data: {
|
||||
labels: ['Esportati', 'Totali LIMS'],
|
||||
datasets: [{
|
||||
data: [exported, notExported],
|
||||
backgroundColor: ['#198754', '#dee2e6'],
|
||||
borderWidth: 1
|
||||
}]
|
||||
},
|
||||
options: {
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
plugins: {
|
||||
legend: {
|
||||
position: 'bottom'
|
||||
},
|
||||
tooltip: {
|
||||
callbacks: {
|
||||
label: function(c) {
|
||||
const perc = limsTotal > 0 ? ((c.parsed / limsTotal) * 100).toFixed(1) : 0;
|
||||
return c.label + ': ' + c.parsed + ' (' + perc + '%)';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Prende i primi N record (già ordinati desc dal backend) per il grafico
|
||||
const CHART_TOP_N = 15;
|
||||
|
||||
function topN(arr) {
|
||||
return arr.slice(0, CHART_TOP_N);
|
||||
}
|
||||
|
||||
function loadStats() {
|
||||
const date = $('#filterDate').val() || todayStr();
|
||||
|
||||
$.getJSON('stats_export_lims_data.php', {
|
||||
date: date
|
||||
}, function(res) {
|
||||
if (!res || !res.success) {
|
||||
Swal.fire('Errore', (res && res.message) || 'Errore nel caricamento', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// KPI
|
||||
$('#kpiExport').text(res.totals.export);
|
||||
$('#kpiClienti').text(res.byClient.length);
|
||||
$('#kpiLimsSamples').text(res.limsSamples ?? 0);
|
||||
$('#limsSamplesInput').val(res.limsSamples ?? '');
|
||||
|
||||
const limsTot = parseInt(res.limsSamples ?? 0, 10);
|
||||
if (limsTot > 0) {
|
||||
const perc = (res.totals.export / limsTot) * 100;
|
||||
$('#kpiPerc').text(perc.toFixed(1) + '%');
|
||||
} else {
|
||||
$('#kpiPerc').text('—');
|
||||
}
|
||||
|
||||
// Torta esportati vs totale
|
||||
renderPie(res.totals.export, limsTot);
|
||||
|
||||
// Grafici
|
||||
const clientTop = topN(res.byClient);
|
||||
const userTop = topN(res.byUser);
|
||||
chartClienti = renderBarChart(chartClienti, 'chartClienti',
|
||||
clientTop.map(r => r.clientname ? (r.clientname + ' (' + r.idclient + ')') : ('ID ' + r.idclient)),
|
||||
clientTop.map(r => r.tot), 'Export');
|
||||
chartUtenti = renderBarChart(chartUtenti, 'chartUtenti',
|
||||
userTop.map(r => r.username ? (r.username + ' (' + r.user_id + ')') : ('ID ' + r.user_id)),
|
||||
userTop.map(r => r.tot), 'Export');
|
||||
|
||||
// Tabelle
|
||||
dtClienti.clear().rows.add(res.byClient).draw();
|
||||
dtUtenti.clear().rows.add(res.byUser).draw();
|
||||
}).fail(function(xhr) {
|
||||
Swal.fire('Errore', 'Richiesta fallita: ' + xhr.status, 'error');
|
||||
});
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
$('#filterDate').val(todayStr());
|
||||
initTables();
|
||||
loadStats();
|
||||
|
||||
$('#btnRefresh').on('click', loadStats);
|
||||
$('#filterDate').on('change', loadStats);
|
||||
|
||||
$('#btnToday').on('click', function() {
|
||||
$('#filterDate').val(todayStr());
|
||||
loadStats();
|
||||
});
|
||||
$('#btnYesterday').on('click', function() {
|
||||
const d = new Date();
|
||||
d.setDate(d.getDate() - 1);
|
||||
const off = d.getTimezoneOffset();
|
||||
$('#filterDate').val(new Date(d.getTime() - off * 60000).toISOString().slice(0, 10));
|
||||
loadStats();
|
||||
});
|
||||
|
||||
$('#btnSaveLimsSamples').on('click', function() {
|
||||
const date = $('#filterDate').val() || todayStr();
|
||||
const total = parseInt($('#limsSamplesInput').val(), 10);
|
||||
if (isNaN(total) || total < 0) {
|
||||
Swal.fire('Attenzione', 'Inserisci un numero valido', 'warning');
|
||||
return;
|
||||
}
|
||||
$.ajax({
|
||||
url: 'stats_save_lims_samples.php',
|
||||
type: 'POST',
|
||||
dataType: 'json',
|
||||
data: {
|
||||
date: date,
|
||||
total_samples: total
|
||||
},
|
||||
success: function(res) {
|
||||
if (res && res.success) {
|
||||
Swal.fire({
|
||||
title: 'Salvato',
|
||||
icon: 'success',
|
||||
timer: 1200,
|
||||
showConfirmButton: false
|
||||
});
|
||||
loadStats();
|
||||
} else {
|
||||
Swal.fire('Errore', (res && res.message) || 'Salvataggio fallito', 'error');
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
Swal.fire('Errore', 'Richiesta fallita: ' + xhr.status, 'error');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
try {
|
||||
// Data richiesta (default oggi). Formato atteso: Y-m-d
|
||||
$date = $_GET['date'] ?? date('Y-m-d');
|
||||
$d = DateTime::createFromFormat('Y-m-d', $date);
|
||||
if (!$d || $d->format('Y-m-d') !== $date) {
|
||||
throw new Exception('Data non valida');
|
||||
}
|
||||
|
||||
// --- Export per cliente (status = 'l', giornata su export_date) ---
|
||||
$stmtC = $pdo->prepare("
|
||||
SELECT d.idclient AS idclient,
|
||||
COUNT(*) AS tot
|
||||
FROM datadb d
|
||||
WHERE d.status = 'l'
|
||||
AND DATE(d.export_date) = :d
|
||||
GROUP BY d.idclient
|
||||
ORDER BY tot DESC
|
||||
");
|
||||
$stmtC->execute(['d' => $date]);
|
||||
$byClient = $stmtC->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// --- Risolvi Nominativo cliente dalla cache locale (search_clienti.php) ---
|
||||
$clientNames = [];
|
||||
$cacheFile = __DIR__ . '/cache/clienti.json';
|
||||
if (is_file($cacheFile)) {
|
||||
$cache = json_decode(file_get_contents($cacheFile), true);
|
||||
foreach (($cache['value'] ?? []) as $c) {
|
||||
$clientNames[(int)($c['IdCliente'] ?? 0)] = trim($c['Nominativo'] ?? '');
|
||||
}
|
||||
}
|
||||
|
||||
// --- Export per utente (status = 'l', giornata su export_date) ---
|
||||
$stmtU = $pdo->prepare("
|
||||
SELECT d.user_id AS user_id,
|
||||
TRIM(CONCAT(COALESCE(u.first_name,''),' ',COALESCE(u.last_name,''))) AS username,
|
||||
COUNT(*) AS tot
|
||||
FROM datadb d
|
||||
LEFT JOIN auth_users u ON u.id = d.user_id
|
||||
WHERE d.status = 'l'
|
||||
AND DATE(d.export_date) = :d
|
||||
GROUP BY d.user_id, username
|
||||
ORDER BY tot DESC
|
||||
");
|
||||
$stmtU->execute(['d' => $date]);
|
||||
$byUser = $stmtU->fetchAll(PDO::FETCH_ASSOC);
|
||||
|
||||
// Normalizza tipi (COUNT torna stringa in PDO)
|
||||
foreach ($byClient as &$r) {
|
||||
$r['idclient'] = $r['idclient'] !== null ? (int)$r['idclient'] : null;
|
||||
$r['tot'] = (int)$r['tot'];
|
||||
$nm = $clientNames[$r['idclient']] ?? '';
|
||||
$r['clientname'] = $nm !== '' ? $nm : null;
|
||||
}
|
||||
unset($r);
|
||||
foreach ($byUser as &$r) {
|
||||
$r['user_id'] = $r['user_id'] !== null ? (int)$r['user_id'] : null;
|
||||
$r['tot'] = (int)$r['tot'];
|
||||
if (isset($r['username'])) $r['username'] = $r['username'] !== null && $r['username'] !== '' ? $r['username'] : null;
|
||||
}
|
||||
unset($r);
|
||||
|
||||
// --- Totale export della giornata ---
|
||||
$totExport = array_sum(array_column($byClient, 'tot'));
|
||||
|
||||
// --- Campioni totali inseriti nel LIMS per la giornata (tabella lims_daily_samples) ---
|
||||
$stmtL = $pdo->prepare("SELECT total_samples FROM lims_daily_samples WHERE sample_date = :d LIMIT 1");
|
||||
$stmtL->execute(['d' => $date]);
|
||||
$limsSamples = $stmtL->fetchColumn();
|
||||
$limsSamples = ($limsSamples !== false) ? (int)$limsSamples : null;
|
||||
|
||||
echo json_encode([
|
||||
'success' => true,
|
||||
'date' => $date,
|
||||
'totals' => ['export' => (int)$totExport],
|
||||
'byClient' => $byClient,
|
||||
'byUser' => $byUser,
|
||||
'limsSamples' => $limsSamples,
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
error_log('Stats export LIMS error: ' . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
include('include/headscript.php');
|
||||
|
||||
header('Content-Type: application/json');
|
||||
|
||||
// Usa la connessione in scrittura se disponibile nel tuo progetto;
|
||||
// qui riuso il pattern del resto dell'app.
|
||||
$dbHandler = DBHandlerSelect::getInstance();
|
||||
$pdo = $dbHandler->getConnection();
|
||||
|
||||
try {
|
||||
$date = $_POST['date'] ?? null;
|
||||
$total = $_POST['total_samples'] ?? null;
|
||||
|
||||
$d = $date ? DateTime::createFromFormat('Y-m-d', $date) : null;
|
||||
if (!$d || $d->format('Y-m-d') !== $date) {
|
||||
throw new Exception('Data non valida');
|
||||
}
|
||||
if (!is_numeric($total) || (int)$total < 0) {
|
||||
throw new Exception('Valore campioni non valido');
|
||||
}
|
||||
$total = (int)$total;
|
||||
|
||||
// Upsert: un solo record per giornata (unique su sample_date)
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO lims_daily_samples (sample_date, total_samples)
|
||||
VALUES (:d, :t)
|
||||
ON DUPLICATE KEY UPDATE total_samples = VALUES(total_samples)
|
||||
");
|
||||
$stmt->execute(['d' => $date, 't' => $total]);
|
||||
|
||||
echo json_encode(['success' => true, 'date' => $date, 'total_samples' => $total]);
|
||||
} catch (Exception $e) {
|
||||
error_log('Save LIMS samples error: ' . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
}
|
||||
@@ -78,51 +78,6 @@
|
||||
color: #198754;
|
||||
}
|
||||
|
||||
.badge-source-pdf {
|
||||
background-color: #fff3cd;
|
||||
color: #b58100;
|
||||
}
|
||||
|
||||
.type-filter-bar {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.type-filter-btn {
|
||||
border: 0;
|
||||
border-radius: 999px;
|
||||
padding: 7px 14px;
|
||||
font-size: 13px;
|
||||
font-weight: 700;
|
||||
color: #fff;
|
||||
opacity: 0.35;
|
||||
transition: all 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.type-filter-btn.active {
|
||||
opacity: 1;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
|
||||
.type-filter-btn[data-type="XLS"] {
|
||||
background-color: #0d6efd;
|
||||
}
|
||||
|
||||
.type-filter-btn[data-type="API"] {
|
||||
background-color: #198754;
|
||||
}
|
||||
|
||||
.type-filter-btn[data-type="PDF"] {
|
||||
background-color: #b58100;
|
||||
}
|
||||
|
||||
.type-filter-btn:hover {
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
|
||||
#xlsTemplatesTable {
|
||||
font-size: 13px;
|
||||
}
|
||||
@@ -181,92 +136,79 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
|
||||
<div class="type-filter-bar">
|
||||
<span class="text-muted fw-semibold me-1">Filter by type:</span>
|
||||
|
||||
<button type="button" class="type-filter-btn active" data-type="XLS">
|
||||
XLS
|
||||
</button>
|
||||
|
||||
<button type="button" class="type-filter-btn active" data-type="API">
|
||||
JSON/API
|
||||
</button>
|
||||
|
||||
<button type="button" class="type-filter-btn active" data-type="PDF">
|
||||
PDF
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="table-responsive">
|
||||
<table id="xlsTemplatesTable" class="table table-striped table-bordered align-middle w-100">
|
||||
<table id="xlsTemplatesTable" class="table table-striped table-bordered table-sm w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Actions</th>
|
||||
<th>Template Name</th>
|
||||
<th><?= htmlspecialchars($action, ENT_QUOTES, 'UTF-8'); ?></th>
|
||||
<th><?= htmlspecialchars($nametemplate, ENT_QUOTES, 'UTF-8'); ?></th>
|
||||
<th>Type</th>
|
||||
<th>Row</th>
|
||||
<th>Col</th>
|
||||
<th>Description</th>
|
||||
<th><?= htmlspecialchars($desctemplate, ENT_QUOTES, 'UTF-8'); ?></th>
|
||||
<th>Client</th>
|
||||
<th>Button</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
<tbody>
|
||||
<!-- DataTables will populate this section automatically -->
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!--end page wrapper -->
|
||||
|
||||
<!--start overlay-->
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<!--end overlay-->
|
||||
|
||||
<!--Start Back To Top Button-->
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<!--End Back To Top Button-->
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<!--end wrapper-->
|
||||
</div>
|
||||
<!--end page wrapper -->
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
<!--start overlay-->
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<!--end overlay-->
|
||||
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
<!--Start Back To Top Button-->
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<!--End Back To Top Button-->
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<!--end wrapper-->
|
||||
|
||||
if (urlParams.get('cloned') === '1') {
|
||||
Swal.fire({
|
||||
title: "Template cloned",
|
||||
text: "The template was cloned successfully.",
|
||||
icon: "success",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
<?php include('jsinclude.php'); ?>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
|
||||
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
|
||||
|
||||
const cleanUrl = window.location.pathname;
|
||||
window.history.replaceState({}, document.title, cleanUrl);
|
||||
}
|
||||
const templatesTable = $('#xlsTemplatesTable').DataTable({
|
||||
processing: true,
|
||||
serverSide: false,
|
||||
ajax: 'load_templates.php',
|
||||
pageLength: 50,
|
||||
autoWidth: false,
|
||||
columns: [{
|
||||
data: 'id',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
title: "Actions",
|
||||
className: "table-actions text-center",
|
||||
render: function(data, type, row) {
|
||||
return `
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
|
||||
if (urlParams.get('cloned') === '1') {
|
||||
Swal.fire({
|
||||
title: "Template cloned",
|
||||
text: "The template was cloned successfully.",
|
||||
icon: "success",
|
||||
confirmButtonText: "OK"
|
||||
});
|
||||
|
||||
const cleanUrl = window.location.pathname;
|
||||
window.history.replaceState({}, document.title, cleanUrl);
|
||||
}
|
||||
$('#xlsTemplatesTable').DataTable({
|
||||
processing: true,
|
||||
serverSide: false,
|
||||
ajax: 'load_templates.php',
|
||||
pageLength: 50,
|
||||
autoWidth: false,
|
||||
columns: [{
|
||||
data: 'id',
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
title: "Actions",
|
||||
className: "table-actions text-center",
|
||||
render: function(data, type, row) {
|
||||
return `
|
||||
<div class="d-flex justify-content-center gap-1">
|
||||
<a href="edit_template_xls.php?id=${data}" class="btn btn-sm btn-primary" title="Edit">
|
||||
<i class="bx bx-edit-alt"></i>
|
||||
@@ -285,169 +227,121 @@
|
||||
</button>
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'name',
|
||||
title: "Template Name",
|
||||
className: "name-cell"
|
||||
},
|
||||
{
|
||||
data: 'source_type',
|
||||
title: "Type",
|
||||
className: "text-center",
|
||||
render: function(data, type, row) {
|
||||
const sourceType = (data || 'XLS').toUpperCase();
|
||||
|
||||
if (type === 'display') {
|
||||
if (sourceType === 'API') {
|
||||
return '<span class="badge-source badge-source-api">API</span>';
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'name',
|
||||
title: "Template Name",
|
||||
className: "name-cell"
|
||||
},
|
||||
{
|
||||
data: 'source_type',
|
||||
title: "Type",
|
||||
className: "text-center",
|
||||
render: function(data, type, row) {
|
||||
let sourceType = (data || 'XLS').toUpperCase();
|
||||
return '<span class="badge-source badge-source-xls">XLS</span>';
|
||||
}
|
||||
|
||||
// Treat JSON as API group for dashboard filter
|
||||
if (sourceType === 'JSON') {
|
||||
sourceType = 'API';
|
||||
}
|
||||
|
||||
if (type === 'display') {
|
||||
if (sourceType === 'API') {
|
||||
return '<span class="badge-source badge-source-api">JSON/API</span>';
|
||||
}
|
||||
|
||||
if (sourceType === 'PDF') {
|
||||
return '<span class="badge-source badge-source-pdf">PDF</span>';
|
||||
}
|
||||
|
||||
return '<span class="badge-source badge-source-xls">XLS</span>';
|
||||
}
|
||||
|
||||
return sourceType;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'header_row',
|
||||
title: "Row",
|
||||
className: "text-center",
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'start_column',
|
||||
title: "Col",
|
||||
className: "text-center",
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'description',
|
||||
title: "Description",
|
||||
className: "description-cell",
|
||||
defaultContent: 'No description'
|
||||
},
|
||||
{
|
||||
data: null,
|
||||
title: "Client",
|
||||
className: "client-cell",
|
||||
render: function(data, type, row) {
|
||||
const clientName = row.clientname || "No client";
|
||||
const clientId = row.idclient || "N/A";
|
||||
return `${clientName} <small class="text-muted">(ID: ${clientId})</small>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'button_label',
|
||||
title: "Button",
|
||||
className: "button-cell",
|
||||
defaultContent: 'Click Me'
|
||||
},
|
||||
{
|
||||
data: 'status',
|
||||
title: "Status",
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: "text-center",
|
||||
render: function(status, type, row) {
|
||||
let checked = (status === "active") ? "checked" : "";
|
||||
return `
|
||||
return sourceType;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'header_row',
|
||||
title: "Row",
|
||||
className: "text-center",
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'start_column',
|
||||
title: "Col",
|
||||
className: "text-center",
|
||||
defaultContent: ''
|
||||
},
|
||||
{
|
||||
data: 'description',
|
||||
title: "Description",
|
||||
className: "description-cell",
|
||||
defaultContent: 'No description'
|
||||
},
|
||||
{
|
||||
data: null,
|
||||
title: "Client",
|
||||
className: "client-cell",
|
||||
render: function(data, type, row) {
|
||||
const clientName = row.clientname || "No client";
|
||||
const clientId = row.idclient || "N/A";
|
||||
return `${clientName} <small class="text-muted">(ID: ${clientId})</small>`;
|
||||
}
|
||||
},
|
||||
{
|
||||
data: 'button_label',
|
||||
title: "Button",
|
||||
className: "button-cell",
|
||||
defaultContent: 'Click Me'
|
||||
},
|
||||
{
|
||||
data: 'status',
|
||||
title: "Status",
|
||||
orderable: false,
|
||||
searchable: false,
|
||||
className: "text-center",
|
||||
render: function(status, type, row) {
|
||||
let checked = (status === "active") ? "checked" : "";
|
||||
return `
|
||||
<label class="switch">
|
||||
<input type="checkbox" class="toggle-status" data-id="${row.id}" ${checked}>
|
||||
<span class="slider round"></span>
|
||||
</label>
|
||||
`;
|
||||
}
|
||||
}
|
||||
],
|
||||
dom: '<"card-header border-bottom p-3"<"d-flex align-items-center"<"card-title mb-0 flex-grow-1"f>>>rt<"card-footer border-top p-3"<"d-flex align-items-center"<"me-auto"l><"d-flex gap-2"ip>>>',
|
||||
lengthMenu: [10, 25, 50, 100],
|
||||
order: [
|
||||
[1, 'asc']
|
||||
],
|
||||
language: {
|
||||
search: "Cerca:",
|
||||
lengthMenu: "Mostra _MENU_ elementi",
|
||||
info: "Visualizzando da _START_ a _END_ di _TOTAL_ elementi",
|
||||
paginate: {
|
||||
first: "<?= isset($langdatatables['paginate_first']) ? $langdatatables['paginate_first'] : 'Primo' ?>",
|
||||
last: "<?= isset($langdatatables['paginate_last']) ? $langdatatables['paginate_last'] : 'Ultimo' ?>",
|
||||
next: "<?= isset($langdatatables['paginate_next']) ? $langdatatables['paginate_next'] : 'Successivo' ?>",
|
||||
previous: "<?= isset($langdatatables['paginate_previous']) ? $langdatatables['paginate_previous'] : 'Precedente' ?>"
|
||||
}
|
||||
}
|
||||
});
|
||||
const activeSourceTypes = {
|
||||
XLS: true,
|
||||
API: true,
|
||||
PDF: true
|
||||
};
|
||||
|
||||
$.fn.dataTable.ext.search.push(function(settings, data, dataIndex) {
|
||||
if (settings.nTable.id !== 'xlsTemplatesTable') {
|
||||
return true;
|
||||
}
|
||||
|
||||
const api = new $.fn.dataTable.Api(settings);
|
||||
const rowData = api.row(dataIndex).data();
|
||||
|
||||
let sourceType = ((rowData && rowData.source_type) ? rowData.source_type : 'XLS').toUpperCase();
|
||||
|
||||
if (sourceType === 'JSON') {
|
||||
sourceType = 'API';
|
||||
}
|
||||
|
||||
return activeSourceTypes[sourceType] === true;
|
||||
});
|
||||
|
||||
$('.type-filter-btn').on('click', function() {
|
||||
const type = $(this).data('type');
|
||||
|
||||
activeSourceTypes[type] = !activeSourceTypes[type];
|
||||
$(this).toggleClass('active', activeSourceTypes[type]);
|
||||
|
||||
const hasAtLeastOneActive = Object.values(activeSourceTypes).some(Boolean);
|
||||
|
||||
if (!hasAtLeastOneActive) {
|
||||
activeSourceTypes[type] = true;
|
||||
$(this).addClass('active');
|
||||
}
|
||||
|
||||
$('#xlsTemplatesTable').DataTable().draw();
|
||||
});
|
||||
});
|
||||
|
||||
function confirmDelete(id) {
|
||||
Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "This action cannot be undone!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#d33",
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
cancelButtonText: "Cancel"
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.href = `delete_template_xls.php?id=${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
],
|
||||
dom: '<"card-header border-bottom p-3"<"d-flex align-items-center"<"card-title mb-0 flex-grow-1"f>>>rt<"card-footer border-top p-3"<"d-flex align-items-center"<"me-auto"l><"d-flex gap-2"ip>>>',
|
||||
lengthMenu: [10, 25, 50, 100],
|
||||
order: [
|
||||
[1, 'asc']
|
||||
],
|
||||
language: {
|
||||
search: "Cerca:",
|
||||
lengthMenu: "Mostra _MENU_ elementi",
|
||||
info: "Visualizzando da _START_ a _END_ di _TOTAL_ elementi",
|
||||
paginate: {
|
||||
first: "<?= isset($langdatatables['paginate_first']) ? $langdatatables['paginate_first'] : 'Primo' ?>",
|
||||
last: "<?= isset($langdatatables['paginate_last']) ? $langdatatables['paginate_last'] : 'Ultimo' ?>",
|
||||
next: "<?= isset($langdatatables['paginate_next']) ? $langdatatables['paginate_next'] : 'Successivo' ?>",
|
||||
previous: "<?= isset($langdatatables['paginate_previous']) ? $langdatatables['paginate_previous'] : 'Precedente' ?>"
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function confirmClone(id, templateName) {
|
||||
Swal.fire({
|
||||
title: "Clone template?",
|
||||
html: `
|
||||
function confirmDelete(id) {
|
||||
Swal.fire({
|
||||
title: "Are you sure?",
|
||||
text: "This action cannot be undone!",
|
||||
icon: "warning",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#d33",
|
||||
cancelButtonColor: "#3085d6",
|
||||
confirmButtonText: "Yes, delete it!",
|
||||
cancelButtonText: "Cancel"
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.href = `delete_template_xls.php?id=${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function confirmClone(id, templateName) {
|
||||
Swal.fire({
|
||||
title: "Clone template?",
|
||||
html: `
|
||||
<div class="text-start">
|
||||
<p class="mb-2">You are about to clone this template:</p>
|
||||
<strong>${templateName}</strong>
|
||||
@@ -456,45 +350,45 @@
|
||||
</p>
|
||||
</div>
|
||||
`,
|
||||
icon: "question",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#ffc107",
|
||||
cancelButtonColor: "#6c757d",
|
||||
confirmButtonText: "Yes, clone it",
|
||||
cancelButtonText: "Cancel"
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.href = `clone_template.php?id=${id}`;
|
||||
}
|
||||
});
|
||||
icon: "question",
|
||||
showCancelButton: true,
|
||||
confirmButtonColor: "#ffc107",
|
||||
cancelButtonColor: "#6c757d",
|
||||
confirmButtonText: "Yes, clone it",
|
||||
cancelButtonText: "Cancel"
|
||||
}).then((result) => {
|
||||
if (result.isConfirmed) {
|
||||
window.location.href = `clone_template.php?id=${id}`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$(document).on("change", ".toggle-status", function() {
|
||||
let templateId = $(this).data("id");
|
||||
let newStatus = $(this).is(":checked") ? "active" : "inactive";
|
||||
$(document).on("change", ".toggle-status", function() {
|
||||
let templateId = $(this).data("id");
|
||||
let newStatus = $(this).is(":checked") ? "active" : "inactive";
|
||||
|
||||
$.ajax({
|
||||
url: "update_template_status.php",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id: templateId,
|
||||
status: newStatus
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
console.log("Status updated successfully.");
|
||||
} else {
|
||||
console.error("Error updating status:", response.message);
|
||||
alert("Error updating status: " + response.message);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error("AJAX error:", xhr.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
$.ajax({
|
||||
url: "update_template_status.php",
|
||||
type: "POST",
|
||||
dataType: "json",
|
||||
data: {
|
||||
id: templateId,
|
||||
status: newStatus
|
||||
},
|
||||
success: function(response) {
|
||||
if (response.success) {
|
||||
console.log("Status updated successfully.");
|
||||
} else {
|
||||
console.error("Error updating status:", response.message);
|
||||
alert("Error updating status: " + response.message);
|
||||
}
|
||||
},
|
||||
error: function(xhr) {
|
||||
console.error("AJAX error:", xhr.responseText);
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
include('include/headscript.php');
|
||||
require_once 'class/db-functions.php';
|
||||
|
||||
$response = ["success" => false, "message" => ""];
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
if (!$pdo) {
|
||||
throw new Exception('Database connection failed.');
|
||||
}
|
||||
|
||||
$userId = $iduserlogin ?? 0;
|
||||
if (!$userId) {
|
||||
throw new Exception('User not authenticated.');
|
||||
}
|
||||
|
||||
$data = json_decode(file_get_contents('php://input'), true);
|
||||
$templateId = isset($data['template_id']) ? (int)$data['template_id'] : 0;
|
||||
$isFavorite = !empty($data['is_favorite']);
|
||||
|
||||
if ($templateId <= 0) {
|
||||
throw new Exception('Invalid template_id.');
|
||||
}
|
||||
|
||||
if ($isFavorite) {
|
||||
// Aggiungi ai preferiti (ignora se già presente)
|
||||
$stmt = $pdo->prepare("
|
||||
INSERT INTO user_template_favorites (user_id, template_id)
|
||||
VALUES (:user_id, :template_id)
|
||||
ON DUPLICATE KEY UPDATE id = id
|
||||
");
|
||||
$stmt->execute(['user_id' => $userId, 'template_id' => $templateId]);
|
||||
} else {
|
||||
// Rimuovi dai preferiti
|
||||
$stmt = $pdo->prepare("
|
||||
DELETE FROM user_template_favorites
|
||||
WHERE user_id = :user_id AND template_id = :template_id
|
||||
");
|
||||
$stmt->execute(['user_id' => $userId, 'template_id' => $templateId]);
|
||||
}
|
||||
|
||||
$response["success"] = true;
|
||||
} catch (PDOException $e) {
|
||||
$response["message"] = "Database error: " . $e->getMessage();
|
||||
} catch (Exception $e) {
|
||||
$response["message"] = "Error: " . $e->getMessage();
|
||||
}
|
||||
|
||||
echo json_encode($response);
|
||||
@@ -3,84 +3,33 @@ header('Content-Type: application/json');
|
||||
|
||||
require_once(__DIR__ . '/include/headscript.php');
|
||||
|
||||
try {
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
|
||||
$id = (int)($input['id'] ?? 0);
|
||||
$field = (string)($input['field'] ?? '');
|
||||
$value = $input['value'] ?? null;
|
||||
$id = (int)($input['id'] ?? 0);
|
||||
$field = (string)($input['field'] ?? '');
|
||||
$value = $input['value'] ?? null;
|
||||
|
||||
if ($id <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$allowed = [
|
||||
'default_value',
|
||||
'default_source',
|
||||
'json_node',
|
||||
'is_visible_import',
|
||||
'is_required'
|
||||
];
|
||||
|
||||
if (!in_array($field, $allowed, true)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid field: ' . $field]);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($field === 'is_visible_import' || $field === 'is_required') {
|
||||
$value = ((int)$value === 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
if ($field === 'default_source') {
|
||||
$value = (string)$value;
|
||||
|
||||
if (!in_array($value, ['manual', 'json'], true)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid default_source']);
|
||||
exit;
|
||||
}
|
||||
|
||||
// If the user goes back to manual, clear the JSON node
|
||||
if ($value === 'manual') {
|
||||
$sql = "
|
||||
UPDATE template_fixed_mapping
|
||||
SET default_source = :val, json_node = NULL
|
||||
WHERE id = :id
|
||||
";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$ok = $stmt->execute([
|
||||
':val' => $value,
|
||||
':id' => $id
|
||||
]);
|
||||
|
||||
echo json_encode(['success' => (bool)$ok]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
if ($field === 'json_node') {
|
||||
$value = trim((string)$value);
|
||||
|
||||
if ($value === '') {
|
||||
$value = null;
|
||||
}
|
||||
}
|
||||
|
||||
$sql = "UPDATE template_fixed_mapping SET {$field} = :val WHERE id = :id";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
|
||||
$ok = $stmt->execute([
|
||||
':val' => $value,
|
||||
':id' => $id
|
||||
]);
|
||||
|
||||
echo json_encode(['success' => (bool)$ok]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode([
|
||||
'success' => false,
|
||||
'message' => $e->getMessage()
|
||||
]);
|
||||
if ($id <= 0) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid id']);
|
||||
exit;
|
||||
}
|
||||
|
||||
$allowed = ['default_value', 'is_visible_import', 'is_required'];
|
||||
if (!in_array($field, $allowed, true)) {
|
||||
echo json_encode(['success' => false, 'message' => 'Invalid field']);
|
||||
exit;
|
||||
}
|
||||
|
||||
if ($field === 'is_visible_import' || $field === 'is_required') {
|
||||
$value = ((int)$value === 1) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
$sql = "UPDATE template_fixed_mapping SET {$field} = :val WHERE id = :id";
|
||||
$stmt = $pdo->prepare($sql);
|
||||
$ok = $stmt->execute([':val' => $value, ':id' => $id]);
|
||||
|
||||
echo json_encode(['success' => (bool)$ok]);
|
||||
|
||||
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
header('Content-Type: application/json');
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
require_once(__DIR__ . '/class/db-functions.php');
|
||||
|
||||
$db = DBHandlerSelect::getInstance();
|
||||
$pdo = $db->getConnection();
|
||||
|
||||
$data = json_decode(file_get_contents("php://input"), true);
|
||||
|
||||
if (!$data || !isset($data['id'], $data['field'])) {
|
||||
echo json_encode(["success" => false, "message" => "Invalid or missing parameters"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$mappingId = (int)$data['id'];
|
||||
$field = $data['field'];
|
||||
$value = isset($data['value']) && $data['value'] ? 1 : 0;
|
||||
|
||||
// Whitelist rigorosa dei campi aggiornabili: evita SQL injection sul nome colonna
|
||||
$allowedFields = ['main_field', 'is_visible_import', 'is_visible_parts', 'is_addnote'];
|
||||
|
||||
if (!in_array($field, $allowedFields, true)) {
|
||||
echo json_encode(["success" => false, "message" => "Invalid field"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
try {
|
||||
// Recupero il template_id della riga per poter applicare i vincoli a livello di template
|
||||
$stmt = $pdo->prepare("SELECT template_id FROM template_mapping WHERE id = ?");
|
||||
$stmt->execute([$mappingId]);
|
||||
$row = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||
|
||||
if (!$row) {
|
||||
echo json_encode(["success" => false, "message" => "Mapping not found"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$templateId = (int)$row['template_id'];
|
||||
|
||||
// Vincolo: max 2 righe con main_field = 1 per template
|
||||
if ($field === 'main_field' && $value === 1) {
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT COUNT(*)
|
||||
FROM template_mapping
|
||||
WHERE template_id = ? AND main_field = '1' AND id != ?
|
||||
");
|
||||
$stmt->execute([$templateId, $mappingId]);
|
||||
$count = (int)$stmt->fetchColumn();
|
||||
|
||||
if ($count >= 2) {
|
||||
echo json_encode([
|
||||
"success" => false,
|
||||
"limit_reached" => true,
|
||||
"message" => "Massimo 2 campi Main consentiti per questo template"
|
||||
]);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
$pdo->beginTransaction();
|
||||
|
||||
$uncheckedOthers = false;
|
||||
|
||||
// Vincolo: solo 1 riga con is_visible_parts = 1 per template (comportamento radio)
|
||||
if ($field === 'is_visible_parts' && $value === 1) {
|
||||
$stmt = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET is_visible_parts = 0
|
||||
WHERE template_id = ? AND id != ? AND is_visible_parts = 1
|
||||
");
|
||||
$stmt->execute([$templateId, $mappingId]);
|
||||
$uncheckedOthers = $stmt->rowCount() > 0;
|
||||
}
|
||||
|
||||
$bindValue = ($field === 'main_field') ? (string)$value : $value;
|
||||
$stmt = $pdo->prepare("UPDATE template_mapping SET {$field} = ? WHERE id = ?");
|
||||
$result = $stmt->execute([$bindValue, $mappingId]);
|
||||
|
||||
if (!$result) {
|
||||
$pdo->rollBack();
|
||||
echo json_encode(["success" => false, "message" => "Database update failed"]);
|
||||
exit;
|
||||
}
|
||||
|
||||
$pdo->commit();
|
||||
|
||||
echo json_encode([
|
||||
"success" => true,
|
||||
"message" => "Flag updated successfully",
|
||||
"unchecked_others" => $uncheckedOthers,
|
||||
"saved" => [
|
||||
"id" => $mappingId,
|
||||
"field" => $field,
|
||||
"value" => $value
|
||||
]
|
||||
]);
|
||||
} catch (Throwable $e) {
|
||||
if ($pdo->inTransaction()) {
|
||||
$pdo->rollBack();
|
||||
}
|
||||
echo json_encode(["success" => false, "message" => "Error: " . $e->getMessage()]);
|
||||
}
|
||||
|
||||
exit;
|
||||
@@ -135,25 +135,26 @@ try {
|
||||
|
||||
// Update existing field AND clear user-entered value if type changed
|
||||
$updateChangedTypeStmt = $pdo->prepare("
|
||||
UPDATE template_mapping
|
||||
SET
|
||||
schema_id = :schema_id,
|
||||
data_type = :data_type,
|
||||
is_required = :is_required,
|
||||
default_value = :default_value,
|
||||
has_list = :has_list,
|
||||
length = :length,
|
||||
decimals = :decimals,
|
||||
min_value = :min_value,
|
||||
max_value = :max_value,
|
||||
default_curr_date = :default_curr_date,
|
||||
tablename = :tablename,
|
||||
field_label = :field_label,
|
||||
UPDATE template_mapping
|
||||
SET
|
||||
schema_id = :schema_id,
|
||||
field_order = :field_order,
|
||||
data_type = :data_type,
|
||||
is_required = :is_required,
|
||||
default_value = :default_value,
|
||||
has_list = :has_list,
|
||||
length = :length,
|
||||
decimals = :decimals,
|
||||
min_value = :min_value,
|
||||
max_value = :max_value,
|
||||
default_curr_date = :default_curr_date,
|
||||
tablename = :tablename,
|
||||
field_label = :field_label,
|
||||
|
||||
manual_default = NULL,
|
||||
auto_value = 'none'
|
||||
manual_default = NULL,
|
||||
auto_value = 'none'
|
||||
|
||||
WHERE id = :id
|
||||
WHERE id = :id
|
||||
");
|
||||
|
||||
$currentFieldIds = [];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,8 +8,71 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<!--favicon-->
|
||||
<link rel="icon" href="assets/images/favicon-32x32.png" type="image/png" />
|
||||
|
||||
<?php include('cssinclude.php'); ?>
|
||||
<title>xxx - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> - User Profile</title>
|
||||
|
||||
<!-- Select2 CSS -->
|
||||
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
|
||||
|
||||
<title>SmartTRF - <?= htmlspecialchars($titlewebsite, ENT_QUOTES, 'UTF-8'); ?> - User Profile</title>
|
||||
|
||||
<style>
|
||||
.profile-avatar-preview {
|
||||
max-width: 100px;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
object-fit: cover;
|
||||
border-radius: 50%;
|
||||
border: 1px solid #e5e7eb;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
|
||||
.small-muted {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.select2-container {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.lims-loading {
|
||||
font-size: 12px;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.password-note {
|
||||
background: #f8fafc;
|
||||
border: 1px solid #e5e7eb;
|
||||
color: #475569;
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.password-error {
|
||||
display: none;
|
||||
background: #fff1f2;
|
||||
border: 1px solid #fecdd3;
|
||||
color: #be123c;
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.password-success {
|
||||
display: none;
|
||||
background: #f0fdf4;
|
||||
border: 1px solid #bbf7d0;
|
||||
color: #166534;
|
||||
border-radius: 10px;
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -18,9 +81,11 @@
|
||||
<!--sidebar wrapper -->
|
||||
<?php include('include/navbar.php'); ?>
|
||||
<!--end sidebar wrapper -->
|
||||
|
||||
<!--start header -->
|
||||
<?php include('include/topbar.php'); ?>
|
||||
<!--end header -->
|
||||
|
||||
<!--start page wrapper -->
|
||||
<div class="page-wrapper">
|
||||
<div class="page-content">
|
||||
@@ -31,61 +96,195 @@
|
||||
<div class="d-flex align-items-center">
|
||||
<div>
|
||||
<h6 class="mb-0">Edit Profile</h6>
|
||||
<small class="text-muted">Update your personal information and LIMS references.</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<form action="update-profile.php" method="POST" enctype="multipart/form-data">
|
||||
<div class="mb-3">
|
||||
<label for="first_name" class="form-label">First Name</label>
|
||||
<input type="text" class="form-control" id="first_name" name="first_name" value="<?= htmlspecialchars($nameuser); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="last_name" class="form-label">Last Name</label>
|
||||
<input type="text" class="form-control" id="last_name" name="last_name" value="<?= htmlspecialchars($surnameuser); ?>" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="<?= htmlspecialchars($emailuser); ?>" required>
|
||||
</div>
|
||||
<form action="update-profile.php" method="POST" enctype="multipart/form-data" id="profileForm">
|
||||
<div class="row g-3">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="lims_user_id" class="form-label">Accettatore</label>
|
||||
<input type="number" class="form-control" id="lims_user_id" name="lims_user_id" value="<?= htmlspecialchars($lims_user_id ?? ''); ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="first_name" class="form-label">First Name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="first_name"
|
||||
name="first_name"
|
||||
value="<?= htmlspecialchars($nameuser ?? '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="lims_global_user_id" class="form-label">LIMS Global</label>
|
||||
<input type="number" class="form-control" id="lims_global_user_id" name="lims_global_user_id" value="<?= htmlspecialchars($lims_global_user_id ?? ''); ?>">
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<label for="last_name" class="form-label">Last Name</label>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="last_name"
|
||||
name="last_name"
|
||||
value="<?= htmlspecialchars($surnameuser ?? '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<label for="email" class="form-label">Email</label>
|
||||
<input
|
||||
type="email"
|
||||
class="form-control"
|
||||
id="email"
|
||||
name="email"
|
||||
value="<?= htmlspecialchars($emailuser ?? '', ENT_QUOTES, 'UTF-8'); ?>"
|
||||
required>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="lims_user_id" class="form-label">Accettatore</label>
|
||||
<select
|
||||
class="form-select"
|
||||
id="lims_user_id"
|
||||
name="lims_user_id"
|
||||
data-current-value="<?= htmlspecialchars($lims_user_id ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<option value="">Select acceptor</option>
|
||||
</select>
|
||||
<div class="small-muted mt-1">
|
||||
Values loaded from CustomField ID 244.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="lims_global_user_id" class="form-label">LIMS Global</label>
|
||||
<select
|
||||
class="form-select"
|
||||
id="lims_global_user_id"
|
||||
name="lims_global_user_id"
|
||||
data-current-value="<?= htmlspecialchars($lims_global_user_id ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
||||
<option value="">Select LIMS user</option>
|
||||
</select>
|
||||
<div class="small-muted mt-1">
|
||||
Values loaded from LIMS users list.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="lims-loading" id="limsLoadingMessage">
|
||||
Loading LIMS users and acceptors...
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<label for="avatar" class="form-label">Profile Picture</label>
|
||||
<input
|
||||
type="file"
|
||||
class="form-control"
|
||||
id="avatar"
|
||||
name="avatar"
|
||||
accept="image/jpeg,image/png,image/webp,image/gif">
|
||||
|
||||
<div class="mt-3 d-flex align-items-center gap-3">
|
||||
<?php if (!empty($photousername)): ?>
|
||||
<img
|
||||
src="../upload/users/<?= htmlspecialchars($photousername, ENT_QUOTES, 'UTF-8'); ?>"
|
||||
alt="Current Avatar"
|
||||
class="profile-avatar-preview"
|
||||
id="avatarPreview">
|
||||
<?php else: ?>
|
||||
<img
|
||||
src="../upload/users/profile.png"
|
||||
alt="Default Avatar"
|
||||
class="profile-avatar-preview"
|
||||
id="avatarPreview">
|
||||
<?php endif; ?>
|
||||
|
||||
<div>
|
||||
<div class="small-muted">Current avatar</div>
|
||||
<div class="small-muted">Allowed formats: JPG, PNG, WEBP, GIF.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="password" class="form-label">New Password</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
autocomplete="new-password">
|
||||
<button
|
||||
class="btn btn-outline-secondary toggle-password"
|
||||
type="button"
|
||||
data-target="password"
|
||||
aria-label="Show or hide password">
|
||||
<i class="bx bx-show"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6">
|
||||
<label for="password_confirm" class="form-label">Confirm New Password</label>
|
||||
<div class="input-group">
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="password_confirm"
|
||||
name="password_confirm"
|
||||
autocomplete="new-password">
|
||||
<button
|
||||
class="btn btn-outline-secondary toggle-password"
|
||||
type="button"
|
||||
data-target="password_confirm"
|
||||
aria-label="Show or hide password confirmation">
|
||||
<i class="bx bx-show"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-12">
|
||||
<div class="password-note" id="passwordNote">
|
||||
<strong>Important:</strong> leave both password fields empty to keep your current password unchanged.
|
||||
Type a new password only if you want to replace it.
|
||||
</div>
|
||||
|
||||
<div class="password-error mt-2" id="passwordError">
|
||||
The two password fields do not match. Please check them before saving.
|
||||
</div>
|
||||
|
||||
<div class="password-success mt-2" id="passwordSuccess">
|
||||
The two passwords match.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input
|
||||
type="hidden"
|
||||
name="iduserlogin"
|
||||
value="<?= htmlspecialchars($iduserlogin ?? '', ENT_QUOTES, 'UTF-8'); ?>">
|
||||
|
||||
<div class="col-md-12">
|
||||
<button type="submit" class="btn btn-primary" id="submitProfileButton">
|
||||
Update Profile
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="avatar" class="form-label">Profile Picture</label>
|
||||
<input type="file" class="form-control" id="avatar" name="avatar">
|
||||
<?php if ($photousername): ?>
|
||||
<p>Current avatar: <img src="..//upload/users/<?= htmlspecialchars($photousername); ?>" alt="Current Avatar" style="max-width: 100px;"></p>
|
||||
<?php else: ?>
|
||||
<p>Current avatar: <img src="..//upload/users/profile.png" alt="Default Avatar" style="max-width: 100px;"></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Password (leave blank to keep current)</label>
|
||||
<input type="password" class="form-control" id="password" name="password">
|
||||
</div>
|
||||
<input type="hidden" name="iduserlogin" value="<?= htmlspecialchars($iduserlogin); ?>">
|
||||
<button type="submit" class="btn btn-primary">Update Profile</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!--end page wrapper -->
|
||||
|
||||
<!--start overlay-->
|
||||
<div class="overlay toggle-icon"></div>
|
||||
<!--end overlay-->
|
||||
|
||||
<!--Start Back To Top Button-->
|
||||
<a href="javaScript:;" class="back-to-top"><i class='bx bxs-up-arrow-alt'></i></a>
|
||||
<a href="javaScript:;" class="back-to-top">
|
||||
<i class='bx bxs-up-arrow-alt'></i>
|
||||
</a>
|
||||
<!--End Back To Top Button-->
|
||||
|
||||
<?php include('include/footer.php'); ?>
|
||||
</div>
|
||||
<!--end wrapper-->
|
||||
@@ -99,7 +298,389 @@
|
||||
<?php //include('include/themeswitcher.php');
|
||||
?>
|
||||
<!--end switcher-->
|
||||
|
||||
<?php include('jsinclude.php'); ?>
|
||||
|
||||
<!-- Select2 JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const profileForm = document.getElementById('profileForm');
|
||||
const submitProfileButton = document.getElementById('submitProfileButton');
|
||||
|
||||
const passwordInput = document.getElementById('password');
|
||||
const passwordConfirmInput = document.getElementById('password_confirm');
|
||||
const passwordError = document.getElementById('passwordError');
|
||||
const passwordSuccess = document.getElementById('passwordSuccess');
|
||||
|
||||
const limsUserSelect = document.getElementById('lims_user_id');
|
||||
const limsGlobalUserSelect = document.getElementById('lims_global_user_id');
|
||||
const limsLoadingMessage = document.getElementById('limsLoadingMessage');
|
||||
const avatarInput = document.getElementById('avatar');
|
||||
const avatarPreview = document.getElementById('avatarPreview');
|
||||
|
||||
const currentLimsUserId = String(limsUserSelect.dataset.currentValue || '');
|
||||
const currentLimsGlobalUserId = String(limsGlobalUserSelect.dataset.currentValue || '');
|
||||
|
||||
const limsGlobalUsersEndpoint = 'get_utenti.php';
|
||||
const limsAcceptorsEndpoint = 'get_customfield_values.php?field_ids=244';
|
||||
|
||||
function validatePasswords(showEmptySuccess = false) {
|
||||
const password = passwordInput.value;
|
||||
const confirmPassword = passwordConfirmInput.value;
|
||||
|
||||
passwordError.style.display = 'none';
|
||||
passwordSuccess.style.display = 'none';
|
||||
passwordInput.classList.remove('is-invalid', 'is-valid');
|
||||
passwordConfirmInput.classList.remove('is-invalid', 'is-valid');
|
||||
|
||||
/*
|
||||
* Both empty means: keep current password.
|
||||
*/
|
||||
if (password === '' && confirmPassword === '') {
|
||||
submitProfileButton.disabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* One field filled and the other empty is not valid.
|
||||
*/
|
||||
if (password === '' || confirmPassword === '') {
|
||||
passwordError.textContent = 'Please fill both password fields, or leave both empty to keep the current password.';
|
||||
passwordError.style.display = 'block';
|
||||
passwordInput.classList.add('is-invalid');
|
||||
passwordConfirmInput.classList.add('is-invalid');
|
||||
submitProfileButton.disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Both filled but different.
|
||||
*/
|
||||
if (password !== confirmPassword) {
|
||||
passwordError.textContent = 'The two password fields do not match. Please check them before saving.';
|
||||
passwordError.style.display = 'block';
|
||||
passwordInput.classList.add('is-invalid');
|
||||
passwordConfirmInput.classList.add('is-invalid');
|
||||
submitProfileButton.disabled = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Both filled and equal.
|
||||
*/
|
||||
passwordInput.classList.add('is-valid');
|
||||
passwordConfirmInput.classList.add('is-valid');
|
||||
passwordSuccess.style.display = showEmptySuccess ? 'block' : 'block';
|
||||
submitProfileButton.disabled = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
passwordInput.addEventListener('input', function() {
|
||||
validatePasswords();
|
||||
});
|
||||
|
||||
passwordConfirmInput.addEventListener('input', function() {
|
||||
validatePasswords();
|
||||
});
|
||||
|
||||
profileForm.addEventListener('submit', function(event) {
|
||||
if (!validatePasswords(true)) {
|
||||
event.preventDefault();
|
||||
|
||||
if (typeof Swal !== 'undefined') {
|
||||
Swal.fire({
|
||||
icon: 'error',
|
||||
title: 'Password mismatch',
|
||||
text: 'Please check the password fields before saving.'
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
function initSelect2() {
|
||||
if (typeof $ === 'undefined') {
|
||||
console.error('jQuery is not loaded. Select2 cannot start.');
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof $.fn.select2 === 'undefined') {
|
||||
console.error('Select2 is not loaded. Check select2.min.js include.');
|
||||
return;
|
||||
}
|
||||
|
||||
$('#lims_user_id').select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select acceptor',
|
||||
allowClear: true,
|
||||
minimumResultsForSearch: 0
|
||||
});
|
||||
|
||||
$('#lims_global_user_id').select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select LIMS user',
|
||||
allowClear: true,
|
||||
minimumResultsForSearch: 0
|
||||
});
|
||||
}
|
||||
|
||||
function destroySelect2IfActive(selector) {
|
||||
if (typeof $ !== 'undefined' && $.fn.select2 && $(selector).hasClass('select2-hidden-accessible')) {
|
||||
$(selector).select2('destroy');
|
||||
}
|
||||
}
|
||||
|
||||
function refreshSelect2() {
|
||||
if (typeof $ === 'undefined' || typeof $.fn.select2 === 'undefined') {
|
||||
return;
|
||||
}
|
||||
|
||||
destroySelect2IfActive('#lims_user_id');
|
||||
destroySelect2IfActive('#lims_global_user_id');
|
||||
|
||||
$('#lims_user_id').select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select acceptor',
|
||||
allowClear: true,
|
||||
minimumResultsForSearch: 0
|
||||
});
|
||||
|
||||
$('#lims_global_user_id').select2({
|
||||
width: '100%',
|
||||
placeholder: 'Select LIMS user',
|
||||
allowClear: true,
|
||||
minimumResultsForSearch: 0
|
||||
});
|
||||
|
||||
$('#lims_user_id').trigger('change');
|
||||
$('#lims_global_user_id').trigger('change');
|
||||
}
|
||||
|
||||
function clearSelect(selectElement, placeholderText) {
|
||||
selectElement.innerHTML = '';
|
||||
|
||||
const option = document.createElement('option');
|
||||
option.value = '';
|
||||
option.textContent = placeholderText;
|
||||
selectElement.appendChild(option);
|
||||
}
|
||||
|
||||
function appendOption(selectElement, value, text) {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
return;
|
||||
}
|
||||
|
||||
const option = document.createElement('option');
|
||||
option.value = String(value);
|
||||
option.textContent = text || String(value);
|
||||
selectElement.appendChild(option);
|
||||
}
|
||||
|
||||
function setSelectValue(selectElement, value) {
|
||||
selectElement.value = value || '';
|
||||
|
||||
if (typeof $ !== 'undefined' && $.fn.select2) {
|
||||
$(selectElement).trigger('change');
|
||||
}
|
||||
}
|
||||
|
||||
function normalizeLimsGlobalUsers(data) {
|
||||
const list = Array.isArray(data) ? data : (data.value || []);
|
||||
const normalized = [];
|
||||
|
||||
list.forEach(function(item) {
|
||||
const id = item.IdUtente ?? item.id ?? item.ID ?? null;
|
||||
const text = item.Nome || item.Nominativo || item.UserName || item.Email || id;
|
||||
|
||||
if (id !== null && id !== undefined && id !== '') {
|
||||
normalized.push({
|
||||
id: String(id),
|
||||
text: String(text)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
normalized.sort(function(a, b) {
|
||||
return String(a.text).localeCompare(String(b.text), 'it', {
|
||||
sensitivity: 'base'
|
||||
});
|
||||
});
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function normalizeAcceptors(data) {
|
||||
const list = data['244'] || data[244] || [];
|
||||
const normalized = [];
|
||||
|
||||
list.forEach(function(item) {
|
||||
const id =
|
||||
item.IdCustomFieldValue ??
|
||||
item.IdCustomFieldsValue ??
|
||||
item.IdCustomFieldValues ??
|
||||
item.Id ??
|
||||
item.ID ??
|
||||
item.id ??
|
||||
item.ValueId ??
|
||||
item.value_id ??
|
||||
item.Codice ??
|
||||
null;
|
||||
|
||||
const text =
|
||||
item.Value ??
|
||||
item.Valore ??
|
||||
item.Nome ??
|
||||
item.Name ??
|
||||
item.Descrizione ??
|
||||
item.Description ??
|
||||
item.Text ??
|
||||
item.text ??
|
||||
item.Label ??
|
||||
item.label ??
|
||||
id;
|
||||
|
||||
if (id !== null && id !== undefined && id !== '') {
|
||||
normalized.push({
|
||||
id: String(id),
|
||||
text: String(text)
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
normalized.sort(function(a, b) {
|
||||
return String(a.text).localeCompare(String(b.text), 'it', {
|
||||
sensitivity: 'base'
|
||||
});
|
||||
});
|
||||
|
||||
return normalized;
|
||||
}
|
||||
|
||||
function populateLimsGlobalUsers(users) {
|
||||
clearSelect(limsGlobalUserSelect, 'Select LIMS user');
|
||||
|
||||
users.forEach(function(user) {
|
||||
appendOption(limsGlobalUserSelect, user.id, user.text);
|
||||
});
|
||||
|
||||
if (currentLimsGlobalUserId !== '') {
|
||||
setSelectValue(limsGlobalUserSelect, currentLimsGlobalUserId);
|
||||
}
|
||||
}
|
||||
|
||||
function populateAcceptors(acceptors) {
|
||||
clearSelect(limsUserSelect, 'Select acceptor');
|
||||
|
||||
acceptors.forEach(function(item) {
|
||||
appendOption(limsUserSelect, item.id, item.text);
|
||||
});
|
||||
|
||||
if (currentLimsUserId !== '') {
|
||||
setSelectValue(limsUserSelect, currentLimsUserId);
|
||||
}
|
||||
}
|
||||
|
||||
async function loadLimsDropdowns() {
|
||||
try {
|
||||
const [globalResponse, acceptorsResponse] = await Promise.all([
|
||||
fetch(limsGlobalUsersEndpoint, {
|
||||
cache: 'no-store'
|
||||
}),
|
||||
fetch(limsAcceptorsEndpoint, {
|
||||
cache: 'no-store'
|
||||
})
|
||||
]);
|
||||
|
||||
if (!globalResponse.ok) {
|
||||
throw new Error('Unable to load LIMS global users.');
|
||||
}
|
||||
|
||||
if (!acceptorsResponse.ok) {
|
||||
throw new Error('Unable to load LIMS acceptors.');
|
||||
}
|
||||
|
||||
const globalData = await globalResponse.json();
|
||||
const acceptorsData = await acceptorsResponse.json();
|
||||
|
||||
const globalUsers = normalizeLimsGlobalUsers(globalData);
|
||||
const acceptors = normalizeAcceptors(acceptorsData);
|
||||
|
||||
populateLimsGlobalUsers(globalUsers);
|
||||
populateAcceptors(acceptors);
|
||||
refreshSelect2();
|
||||
|
||||
if (limsLoadingMessage) {
|
||||
limsLoadingMessage.textContent = 'LIMS users and acceptors loaded.';
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
|
||||
if (limsLoadingMessage) {
|
||||
limsLoadingMessage.textContent = 'Warning: unable to load LIMS users or acceptors.';
|
||||
}
|
||||
|
||||
if (typeof Swal !== 'undefined') {
|
||||
Swal.fire({
|
||||
icon: 'warning',
|
||||
title: 'LIMS lists not loaded',
|
||||
text: error.message || 'Unable to load LIMS dropdown values.'
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
avatarInput.addEventListener('change', function(event) {
|
||||
const file = event.target.files[0];
|
||||
|
||||
if (!file) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = function(e) {
|
||||
avatarPreview.src = e.target.result;
|
||||
};
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
});
|
||||
document.querySelectorAll('.toggle-password').forEach(function(button) {
|
||||
button.addEventListener('click', function() {
|
||||
const targetId = button.dataset.target;
|
||||
const input = document.getElementById(targetId);
|
||||
const icon = button.querySelector('i');
|
||||
|
||||
if (!input) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
|
||||
if (icon) {
|
||||
icon.classList.remove('bx-show');
|
||||
icon.classList.add('bx-hide');
|
||||
}
|
||||
} else {
|
||||
input.type = 'password';
|
||||
|
||||
if (icon) {
|
||||
icon.classList.remove('bx-hide');
|
||||
icon.classList.add('bx-show');
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
initSelect2();
|
||||
loadLimsDropdowns();
|
||||
validatePasswords();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -1,4 +1,5 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Validate rows before export to LIMS.
|
||||
*
|
||||
@@ -128,6 +129,7 @@ $fixedAliasMap = [
|
||||
'ClienteResponsabile' => 'cliente_responsabile_id',
|
||||
'ClienteFornitore' => 'cliente_fornitore_id',
|
||||
'ClienteAnalisi' => 'clienteAnalisi',
|
||||
'ClienteFatturazione' => 'ClienteFatturazione',
|
||||
'MoltiplicatorePrezzo' => 'moltiplicatore_prezzo_id',
|
||||
'AnagraficaCertestObject' => 'anagrafica_certest_object_id',
|
||||
'AnagraficaCertestService' => 'anagrafica_certest_service_id',
|
||||
@@ -136,9 +138,11 @@ $fixedAliasMap = [
|
||||
|
||||
// Fixed keys NOT enforced by the generic mandatory check above:
|
||||
// - ConsegnaRichiesta: handled by its dedicated validator (also checks the date)
|
||||
// - ClienteFornitore / ClienteAnalisi: nullable placeholders, sent as null on
|
||||
// export and accepted by LIMS.
|
||||
$skipRequiredFixed = ['ConsegnaRichiesta', 'ClienteFornitore', 'ClienteAnalisi'];
|
||||
// - ClienteFornitore: nullable placeholder, sent as null on export and
|
||||
// accepted by LIMS.
|
||||
// - ClienteFatturazione: TEMP disabled - problemi API lato LIMS, non viene più
|
||||
// inviato in save_export_lims.php, quindi non deve bloccare la validazione.
|
||||
$skipRequiredFixed = ['ConsegnaRichiesta', 'ClienteFornitore', 'ClienteFatturazione'];
|
||||
|
||||
// ── Main ────────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -161,7 +165,7 @@ try {
|
||||
SELECT iddatadb, templateid, consegna_richiesta,
|
||||
cliente_responsabile_id, moltiplicatore_prezzo_id,
|
||||
anagrafica_certest_object_id, anagrafica_certest_service_id,
|
||||
cliente_fornitore_id, clienteAnalisi
|
||||
cliente_fornitore_id, clienteAnalisi, ClienteFatturazione
|
||||
FROM datadb
|
||||
WHERE iddatadb IN ($placeholders)
|
||||
");
|
||||
@@ -176,6 +180,8 @@ try {
|
||||
SELECT iddatadb, part_number, part_description, idmatrice
|
||||
FROM identification_parts
|
||||
WHERE iddatadb IN ($placeholders)
|
||||
AND part_description IS NOT NULL
|
||||
AND TRIM(part_description) <> ''
|
||||
");
|
||||
$stmt->execute($iddatadbList);
|
||||
$partsInfo = [];
|
||||
@@ -185,7 +191,7 @@ try {
|
||||
|
||||
// Mandatory-field config per template
|
||||
$templateIds = array_values(array_unique(array_filter(array_map(
|
||||
fn ($r) => (int)($r['templateid'] ?? 0),
|
||||
fn($r) => (int)($r['templateid'] ?? 0),
|
||||
$recordsInfo
|
||||
))));
|
||||
|
||||
@@ -210,13 +216,14 @@ try {
|
||||
$requiredFixedByTemplate[(int)$r['template_id']][$key] = $key;
|
||||
}
|
||||
|
||||
// Required custom fields that are visible in the import grid
|
||||
// Required custom fields that are visible in the import grid (excluding filed_id = 189 Tested component)
|
||||
$stmt = $pdo->prepare("
|
||||
SELECT id AS mapping_id, template_id, field_label
|
||||
FROM template_mapping
|
||||
WHERE template_id IN ($tplPlaceholders)
|
||||
AND is_required = 1
|
||||
AND is_visible_import = 1
|
||||
AND is_required = 1
|
||||
AND is_visible_import = 1
|
||||
AND id <> 189
|
||||
");
|
||||
$stmt->execute($templateIds);
|
||||
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $r) {
|
||||
@@ -272,7 +279,6 @@ try {
|
||||
}
|
||||
|
||||
echo json_encode(['success' => true, 'results' => $results]);
|
||||
|
||||
} catch (Exception $e) {
|
||||
error_log("Validation error: " . $e->getMessage());
|
||||
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
|
||||
|
||||
@@ -26,7 +26,8 @@ $cacheDir = __DIR__ . '/cache';
|
||||
if (!is_dir($cacheDir)) mkdir($cacheDir, 0777, true);
|
||||
|
||||
$log = [];
|
||||
function warmLog(string $msg, bool $isCli) {
|
||||
function warmLog(string $msg, bool $isCli)
|
||||
{
|
||||
global $log;
|
||||
$line = date('H:i:s') . " $msg";
|
||||
$log[] = $line;
|
||||
@@ -39,7 +40,7 @@ try {
|
||||
|
||||
// 1. Clients
|
||||
warmLog('[clients] Fetching...', $isCli);
|
||||
$params = ['$select' => 'IdCliente,Nominativo,CodiceCliente', '$orderby' => 'Nominativo asc'];
|
||||
$params = ['$select' => 'IdCliente,Nominativo,CodiceCliente,Inattivo', '$orderby' => 'Nominativo asc'];
|
||||
$clientData = $api->get("Cliente?" . http_build_query($params));
|
||||
file_put_contents($cacheDir . '/clienti.json', json_encode($clientData));
|
||||
$clientCount = count($clientData['value'] ?? []);
|
||||
@@ -112,9 +113,28 @@ try {
|
||||
file_put_contents($cacheDir . '/matrici.json', json_encode(['success' => true, 'value' => $matrici]));
|
||||
warmLog("[matrici] Cached " . count($matrici) . " items", $isCli);
|
||||
|
||||
// 6. Analisi — only for matrici actually used by parts (warming all ~2500 matrici
|
||||
// would mean one API call each; the used set is a few dozen).
|
||||
$stmt = $pdo->query("SELECT DISTINCT idmatrice FROM identification_parts WHERE idmatrice IS NOT NULL AND idmatrice > 0 ORDER BY idmatrice ASC");
|
||||
$matriceIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
|
||||
warmLog("[analisi] Fetching for " . count($matriceIds) . " matrici in use...", $isCli);
|
||||
|
||||
foreach ($matriceIds as $mid) {
|
||||
$mid = (int)$mid;
|
||||
// Same query as get_analisi_matrice_filter.php with web_only=1 (what the analysis modal requests)
|
||||
$filter = rawurlencode("Matrice/IdMatrice eq $mid and SelezionabileSuWeb eq true");
|
||||
try {
|
||||
$data = $api->get("Analisi?\$filter={$filter}");
|
||||
$values = $data['value'] ?? [];
|
||||
file_put_contents($cacheDir . '/analisi_matrice_' . $mid . '.json', json_encode(['value' => $values]));
|
||||
warmLog("[analisi] Matrice $mid: " . count($values) . " analisi", $isCli);
|
||||
} catch (Exception $e) {
|
||||
warmLog("[analisi] Matrice $mid: ERROR " . $e->getMessage(), $isCli);
|
||||
}
|
||||
}
|
||||
|
||||
$elapsed = round(microtime(true) - $startTime, 1);
|
||||
warmLog("Done in {$elapsed}s", $isCli);
|
||||
|
||||
} catch (Exception $e) {
|
||||
warmLog("FATAL: " . $e->getMessage(), $isCli);
|
||||
}
|
||||
|
||||
Vendored
+1
-4
@@ -14,10 +14,7 @@ if (PHP_VERSION_ID < 50600) {
|
||||
echo $err;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
$err,
|
||||
E_USER_ERROR
|
||||
);
|
||||
throw new RuntimeException($err);
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/composer/autoload_real.php';
|
||||
|
||||
+41
-4
@@ -26,12 +26,23 @@ use Composer\Semver\VersionParser;
|
||||
*/
|
||||
class InstalledVersions
|
||||
{
|
||||
/**
|
||||
* @var string|null if set (by reflection by Composer), this should be set to the path where this class is being copied to
|
||||
* @internal
|
||||
*/
|
||||
private static $selfDir = null;
|
||||
|
||||
/**
|
||||
* @var mixed[]|null
|
||||
* @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>}|array{}|null
|
||||
*/
|
||||
private static $installed;
|
||||
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
private static $installedIsLocalDir;
|
||||
|
||||
/**
|
||||
* @var bool|null
|
||||
*/
|
||||
@@ -309,6 +320,24 @@ class InstalledVersions
|
||||
{
|
||||
self::$installed = $data;
|
||||
self::$installedByVendor = array();
|
||||
|
||||
// when using reload, we disable the duplicate protection to ensure that self::$installed data is
|
||||
// always returned, but we cannot know whether it comes from the installed.php in __DIR__ or not,
|
||||
// so we have to assume it does not, and that may result in duplicate data being returned when listing
|
||||
// all installed packages for example
|
||||
self::$installedIsLocalDir = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
private static function getSelfDir()
|
||||
{
|
||||
if (self::$selfDir === null) {
|
||||
self::$selfDir = strtr(__DIR__, '\\', '/');
|
||||
}
|
||||
|
||||
return self::$selfDir;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -322,19 +351,27 @@ class InstalledVersions
|
||||
}
|
||||
|
||||
$installed = array();
|
||||
$copiedLocalDir = false;
|
||||
|
||||
if (self::$canGetVendors) {
|
||||
$selfDir = self::getSelfDir();
|
||||
foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) {
|
||||
$vendorDir = strtr($vendorDir, '\\', '/');
|
||||
if (isset(self::$installedByVendor[$vendorDir])) {
|
||||
$installed[] = self::$installedByVendor[$vendorDir];
|
||||
} elseif (is_file($vendorDir.'/composer/installed.php')) {
|
||||
/** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array<string, array{pretty_version?: string, version?: string, reference?: string|null, type?: string, install_path?: string, aliases?: string[], dev_requirement: bool, replaced?: string[], provided?: string[]}>} $required */
|
||||
$required = require $vendorDir.'/composer/installed.php';
|
||||
$installed[] = self::$installedByVendor[$vendorDir] = $required;
|
||||
if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) {
|
||||
self::$installed = $installed[count($installed) - 1];
|
||||
self::$installedByVendor[$vendorDir] = $required;
|
||||
$installed[] = $required;
|
||||
if (self::$installed === null && $vendorDir.'/composer' === $selfDir) {
|
||||
self::$installed = $required;
|
||||
self::$installedIsLocalDir = true;
|
||||
}
|
||||
}
|
||||
if (self::$installedIsLocalDir && $vendorDir.'/composer' === $selfDir) {
|
||||
$copiedLocalDir = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -350,7 +387,7 @@ class InstalledVersions
|
||||
}
|
||||
}
|
||||
|
||||
if (self::$installed !== array()) {
|
||||
if (self::$installed !== array() && !$copiedLocalDir) {
|
||||
$installed[] = self::$installed;
|
||||
}
|
||||
|
||||
|
||||
+407
@@ -184,6 +184,196 @@ return array(
|
||||
'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
|
||||
'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => $vendorDir . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
|
||||
'Brick\\Math\\RoundingMode' => $vendorDir . '/brick/math/src/RoundingMode.php',
|
||||
'Cake\\Chronos\\Chronos' => $vendorDir . '/cakephp/chronos/src/Chronos.php',
|
||||
'Cake\\Chronos\\ChronosDate' => $vendorDir . '/cakephp/chronos/src/ChronosDate.php',
|
||||
'Cake\\Chronos\\ChronosDatePeriod' => $vendorDir . '/cakephp/chronos/src/ChronosDatePeriod.php',
|
||||
'Cake\\Chronos\\ChronosInterval' => $vendorDir . '/cakephp/chronos/src/ChronosInterval.php',
|
||||
'Cake\\Chronos\\ChronosPeriod' => $vendorDir . '/cakephp/chronos/src/ChronosPeriod.php',
|
||||
'Cake\\Chronos\\ChronosTime' => $vendorDir . '/cakephp/chronos/src/ChronosTime.php',
|
||||
'Cake\\Chronos\\ClockFactory' => $vendorDir . '/cakephp/chronos/src/ClockFactory.php',
|
||||
'Cake\\Chronos\\DifferenceFormatter' => $vendorDir . '/cakephp/chronos/src/DifferenceFormatter.php',
|
||||
'Cake\\Chronos\\DifferenceFormatterInterface' => $vendorDir . '/cakephp/chronos/src/DifferenceFormatterInterface.php',
|
||||
'Cake\\Chronos\\FormattingTrait' => $vendorDir . '/cakephp/chronos/src/FormattingTrait.php',
|
||||
'Cake\\Chronos\\Translator' => $vendorDir . '/cakephp/chronos/src/Translator.php',
|
||||
'Cake\\Core\\App' => $vendorDir . '/cakephp/core/App.php',
|
||||
'Cake\\Core\\Attribute\\Configure' => $vendorDir . '/cakephp/core/Attribute/Configure.php',
|
||||
'Cake\\Core\\BasePlugin' => $vendorDir . '/cakephp/core/BasePlugin.php',
|
||||
'Cake\\Core\\Configure' => $vendorDir . '/cakephp/core/Configure.php',
|
||||
'Cake\\Core\\Configure\\ConfigEngineInterface' => $vendorDir . '/cakephp/core/Configure/ConfigEngineInterface.php',
|
||||
'Cake\\Core\\Configure\\Engine\\IniConfig' => $vendorDir . '/cakephp/core/Configure/Engine/IniConfig.php',
|
||||
'Cake\\Core\\Configure\\Engine\\JsonConfig' => $vendorDir . '/cakephp/core/Configure/Engine/JsonConfig.php',
|
||||
'Cake\\Core\\Configure\\Engine\\PhpConfig' => $vendorDir . '/cakephp/core/Configure/Engine/PhpConfig.php',
|
||||
'Cake\\Core\\Configure\\FileConfigTrait' => $vendorDir . '/cakephp/core/Configure/FileConfigTrait.php',
|
||||
'Cake\\Core\\ConsoleApplicationInterface' => $vendorDir . '/cakephp/core/ConsoleApplicationInterface.php',
|
||||
'Cake\\Core\\Container' => $vendorDir . '/cakephp/core/Container.php',
|
||||
'Cake\\Core\\ContainerApplicationInterface' => $vendorDir . '/cakephp/core/ContainerApplicationInterface.php',
|
||||
'Cake\\Core\\ContainerInterface' => $vendorDir . '/cakephp/core/ContainerInterface.php',
|
||||
'Cake\\Core\\ConventionsTrait' => $vendorDir . '/cakephp/core/ConventionsTrait.php',
|
||||
'Cake\\Core\\EventAwareApplicationInterface' => $vendorDir . '/cakephp/core/EventAwareApplicationInterface.php',
|
||||
'Cake\\Core\\Exception\\CakeException' => $vendorDir . '/cakephp/core/Exception/CakeException.php',
|
||||
'Cake\\Core\\Exception\\HttpErrorCodeInterface' => $vendorDir . '/cakephp/core/Exception/HttpErrorCodeInterface.php',
|
||||
'Cake\\Core\\Exception\\MissingPluginException' => $vendorDir . '/cakephp/core/Exception/MissingPluginException.php',
|
||||
'Cake\\Core\\HttpApplicationInterface' => $vendorDir . '/cakephp/core/HttpApplicationInterface.php',
|
||||
'Cake\\Core\\InstanceConfigTrait' => $vendorDir . '/cakephp/core/InstanceConfigTrait.php',
|
||||
'Cake\\Core\\ObjectRegistry' => $vendorDir . '/cakephp/core/ObjectRegistry.php',
|
||||
'Cake\\Core\\Plugin' => $vendorDir . '/cakephp/core/Plugin.php',
|
||||
'Cake\\Core\\PluginApplicationInterface' => $vendorDir . '/cakephp/core/PluginApplicationInterface.php',
|
||||
'Cake\\Core\\PluginCollection' => $vendorDir . '/cakephp/core/PluginCollection.php',
|
||||
'Cake\\Core\\PluginConfig' => $vendorDir . '/cakephp/core/PluginConfig.php',
|
||||
'Cake\\Core\\PluginInterface' => $vendorDir . '/cakephp/core/PluginInterface.php',
|
||||
'Cake\\Core\\Retry\\CommandRetry' => $vendorDir . '/cakephp/core/Retry/CommandRetry.php',
|
||||
'Cake\\Core\\Retry\\RetryStrategyInterface' => $vendorDir . '/cakephp/core/Retry/RetryStrategyInterface.php',
|
||||
'Cake\\Core\\ServiceConfig' => $vendorDir . '/cakephp/core/ServiceConfig.php',
|
||||
'Cake\\Core\\ServiceProvider' => $vendorDir . '/cakephp/core/ServiceProvider.php',
|
||||
'Cake\\Core\\StaticConfigTrait' => $vendorDir . '/cakephp/core/StaticConfigTrait.php',
|
||||
'Cake\\Core\\TestSuite\\ContainerStubTrait' => $vendorDir . '/cakephp/core/TestSuite/ContainerStubTrait.php',
|
||||
'Cake\\Database\\Connection' => $vendorDir . '/cakephp/database/Connection.php',
|
||||
'Cake\\Database\\ConstraintsInterface' => $vendorDir . '/cakephp/database/ConstraintsInterface.php',
|
||||
'Cake\\Database\\Driver' => $vendorDir . '/cakephp/database/Driver.php',
|
||||
'Cake\\Database\\DriverFeatureEnum' => $vendorDir . '/cakephp/database/DriverFeatureEnum.php',
|
||||
'Cake\\Database\\Driver\\Mysql' => $vendorDir . '/cakephp/database/Driver/Mysql.php',
|
||||
'Cake\\Database\\Driver\\Postgres' => $vendorDir . '/cakephp/database/Driver/Postgres.php',
|
||||
'Cake\\Database\\Driver\\Sqlite' => $vendorDir . '/cakephp/database/Driver/Sqlite.php',
|
||||
'Cake\\Database\\Driver\\Sqlserver' => $vendorDir . '/cakephp/database/Driver/Sqlserver.php',
|
||||
'Cake\\Database\\Driver\\TupleComparisonTranslatorTrait' => $vendorDir . '/cakephp/database/Driver/TupleComparisonTranslatorTrait.php',
|
||||
'Cake\\Database\\Exception\\DatabaseException' => $vendorDir . '/cakephp/database/Exception/DatabaseException.php',
|
||||
'Cake\\Database\\Exception\\MissingConnectionException' => $vendorDir . '/cakephp/database/Exception/MissingConnectionException.php',
|
||||
'Cake\\Database\\Exception\\MissingDriverException' => $vendorDir . '/cakephp/database/Exception/MissingDriverException.php',
|
||||
'Cake\\Database\\Exception\\MissingExtensionException' => $vendorDir . '/cakephp/database/Exception/MissingExtensionException.php',
|
||||
'Cake\\Database\\Exception\\NestedTransactionRollbackException' => $vendorDir . '/cakephp/database/Exception/NestedTransactionRollbackException.php',
|
||||
'Cake\\Database\\Exception\\QueryException' => $vendorDir . '/cakephp/database/Exception/QueryException.php',
|
||||
'Cake\\Database\\ExpressionInterface' => $vendorDir . '/cakephp/database/ExpressionInterface.php',
|
||||
'Cake\\Database\\Expression\\AggregateExpression' => $vendorDir . '/cakephp/database/Expression/AggregateExpression.php',
|
||||
'Cake\\Database\\Expression\\BetweenExpression' => $vendorDir . '/cakephp/database/Expression/BetweenExpression.php',
|
||||
'Cake\\Database\\Expression\\CaseExpressionTrait' => $vendorDir . '/cakephp/database/Expression/CaseExpressionTrait.php',
|
||||
'Cake\\Database\\Expression\\CaseStatementExpression' => $vendorDir . '/cakephp/database/Expression/CaseStatementExpression.php',
|
||||
'Cake\\Database\\Expression\\CommonTableExpression' => $vendorDir . '/cakephp/database/Expression/CommonTableExpression.php',
|
||||
'Cake\\Database\\Expression\\ComparisonExpression' => $vendorDir . '/cakephp/database/Expression/ComparisonExpression.php',
|
||||
'Cake\\Database\\Expression\\FieldInterface' => $vendorDir . '/cakephp/database/Expression/FieldInterface.php',
|
||||
'Cake\\Database\\Expression\\FieldTrait' => $vendorDir . '/cakephp/database/Expression/FieldTrait.php',
|
||||
'Cake\\Database\\Expression\\FunctionExpression' => $vendorDir . '/cakephp/database/Expression/FunctionExpression.php',
|
||||
'Cake\\Database\\Expression\\IdentifierExpression' => $vendorDir . '/cakephp/database/Expression/IdentifierExpression.php',
|
||||
'Cake\\Database\\Expression\\OrderByExpression' => $vendorDir . '/cakephp/database/Expression/OrderByExpression.php',
|
||||
'Cake\\Database\\Expression\\OrderClauseExpression' => $vendorDir . '/cakephp/database/Expression/OrderClauseExpression.php',
|
||||
'Cake\\Database\\Expression\\QueryExpression' => $vendorDir . '/cakephp/database/Expression/QueryExpression.php',
|
||||
'Cake\\Database\\Expression\\StringExpression' => $vendorDir . '/cakephp/database/Expression/StringExpression.php',
|
||||
'Cake\\Database\\Expression\\TupleComparison' => $vendorDir . '/cakephp/database/Expression/TupleComparison.php',
|
||||
'Cake\\Database\\Expression\\UnaryExpression' => $vendorDir . '/cakephp/database/Expression/UnaryExpression.php',
|
||||
'Cake\\Database\\Expression\\ValuesExpression' => $vendorDir . '/cakephp/database/Expression/ValuesExpression.php',
|
||||
'Cake\\Database\\Expression\\WhenThenExpression' => $vendorDir . '/cakephp/database/Expression/WhenThenExpression.php',
|
||||
'Cake\\Database\\Expression\\WindowExpression' => $vendorDir . '/cakephp/database/Expression/WindowExpression.php',
|
||||
'Cake\\Database\\Expression\\WindowInterface' => $vendorDir . '/cakephp/database/Expression/WindowInterface.php',
|
||||
'Cake\\Database\\FieldTypeConverter' => $vendorDir . '/cakephp/database/FieldTypeConverter.php',
|
||||
'Cake\\Database\\FunctionsBuilder' => $vendorDir . '/cakephp/database/FunctionsBuilder.php',
|
||||
'Cake\\Database\\IdentifierQuoter' => $vendorDir . '/cakephp/database/IdentifierQuoter.php',
|
||||
'Cake\\Database\\Log\\LoggedQuery' => $vendorDir . '/cakephp/database/Log/LoggedQuery.php',
|
||||
'Cake\\Database\\Log\\QueryLogger' => $vendorDir . '/cakephp/database/Log/QueryLogger.php',
|
||||
'Cake\\Database\\PostgresCompiler' => $vendorDir . '/cakephp/database/PostgresCompiler.php',
|
||||
'Cake\\Database\\Query' => $vendorDir . '/cakephp/database/Query.php',
|
||||
'Cake\\Database\\QueryCompiler' => $vendorDir . '/cakephp/database/QueryCompiler.php',
|
||||
'Cake\\Database\\Query\\DeleteQuery' => $vendorDir . '/cakephp/database/Query/DeleteQuery.php',
|
||||
'Cake\\Database\\Query\\InsertQuery' => $vendorDir . '/cakephp/database/Query/InsertQuery.php',
|
||||
'Cake\\Database\\Query\\QueryFactory' => $vendorDir . '/cakephp/database/Query/QueryFactory.php',
|
||||
'Cake\\Database\\Query\\SelectQuery' => $vendorDir . '/cakephp/database/Query/SelectQuery.php',
|
||||
'Cake\\Database\\Query\\UpdateQuery' => $vendorDir . '/cakephp/database/Query/UpdateQuery.php',
|
||||
'Cake\\Database\\Retry\\ErrorCodeWaitStrategy' => $vendorDir . '/cakephp/database/Retry/ErrorCodeWaitStrategy.php',
|
||||
'Cake\\Database\\Retry\\ReconnectStrategy' => $vendorDir . '/cakephp/database/Retry/ReconnectStrategy.php',
|
||||
'Cake\\Database\\SchemaCache' => $vendorDir . '/cakephp/database/SchemaCache.php',
|
||||
'Cake\\Database\\Schema\\CachedCollection' => $vendorDir . '/cakephp/database/Schema/CachedCollection.php',
|
||||
'Cake\\Database\\Schema\\CheckConstraint' => $vendorDir . '/cakephp/database/Schema/CheckConstraint.php',
|
||||
'Cake\\Database\\Schema\\Collection' => $vendorDir . '/cakephp/database/Schema/Collection.php',
|
||||
'Cake\\Database\\Schema\\CollectionInterface' => $vendorDir . '/cakephp/database/Schema/CollectionInterface.php',
|
||||
'Cake\\Database\\Schema\\Column' => $vendorDir . '/cakephp/database/Schema/Column.php',
|
||||
'Cake\\Database\\Schema\\Constraint' => $vendorDir . '/cakephp/database/Schema/Constraint.php',
|
||||
'Cake\\Database\\Schema\\ForeignKey' => $vendorDir . '/cakephp/database/Schema/ForeignKey.php',
|
||||
'Cake\\Database\\Schema\\Index' => $vendorDir . '/cakephp/database/Schema/Index.php',
|
||||
'Cake\\Database\\Schema\\MysqlSchemaDialect' => $vendorDir . '/cakephp/database/Schema/MysqlSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\PostgresSchemaDialect' => $vendorDir . '/cakephp/database/Schema/PostgresSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\SchemaDialect' => $vendorDir . '/cakephp/database/Schema/SchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\SqlGeneratorInterface' => $vendorDir . '/cakephp/database/Schema/SqlGeneratorInterface.php',
|
||||
'Cake\\Database\\Schema\\SqliteSchemaDialect' => $vendorDir . '/cakephp/database/Schema/SqliteSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\SqlserverSchemaDialect' => $vendorDir . '/cakephp/database/Schema/SqlserverSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\TableSchema' => $vendorDir . '/cakephp/database/Schema/TableSchema.php',
|
||||
'Cake\\Database\\Schema\\TableSchemaInterface' => $vendorDir . '/cakephp/database/Schema/TableSchemaInterface.php',
|
||||
'Cake\\Database\\Schema\\UniqueKey' => $vendorDir . '/cakephp/database/Schema/UniqueKey.php',
|
||||
'Cake\\Database\\SqlserverCompiler' => $vendorDir . '/cakephp/database/SqlserverCompiler.php',
|
||||
'Cake\\Database\\StatementInterface' => $vendorDir . '/cakephp/database/StatementInterface.php',
|
||||
'Cake\\Database\\Statement\\SqliteStatement' => $vendorDir . '/cakephp/database/Statement/SqliteStatement.php',
|
||||
'Cake\\Database\\Statement\\SqlserverStatement' => $vendorDir . '/cakephp/database/Statement/SqlserverStatement.php',
|
||||
'Cake\\Database\\Statement\\Statement' => $vendorDir . '/cakephp/database/Statement/Statement.php',
|
||||
'Cake\\Database\\TypeFactory' => $vendorDir . '/cakephp/database/TypeFactory.php',
|
||||
'Cake\\Database\\TypeInterface' => $vendorDir . '/cakephp/database/TypeInterface.php',
|
||||
'Cake\\Database\\TypeMap' => $vendorDir . '/cakephp/database/TypeMap.php',
|
||||
'Cake\\Database\\TypeMapTrait' => $vendorDir . '/cakephp/database/TypeMapTrait.php',
|
||||
'Cake\\Database\\Type\\BaseType' => $vendorDir . '/cakephp/database/Type/BaseType.php',
|
||||
'Cake\\Database\\Type\\BatchCastingInterface' => $vendorDir . '/cakephp/database/Type/BatchCastingInterface.php',
|
||||
'Cake\\Database\\Type\\BinaryType' => $vendorDir . '/cakephp/database/Type/BinaryType.php',
|
||||
'Cake\\Database\\Type\\BinaryUuidType' => $vendorDir . '/cakephp/database/Type/BinaryUuidType.php',
|
||||
'Cake\\Database\\Type\\BoolType' => $vendorDir . '/cakephp/database/Type/BoolType.php',
|
||||
'Cake\\Database\\Type\\ColumnSchemaAwareInterface' => $vendorDir . '/cakephp/database/Type/ColumnSchemaAwareInterface.php',
|
||||
'Cake\\Database\\Type\\DateTimeFractionalType' => $vendorDir . '/cakephp/database/Type/DateTimeFractionalType.php',
|
||||
'Cake\\Database\\Type\\DateTimeTimezoneType' => $vendorDir . '/cakephp/database/Type/DateTimeTimezoneType.php',
|
||||
'Cake\\Database\\Type\\DateTimeType' => $vendorDir . '/cakephp/database/Type/DateTimeType.php',
|
||||
'Cake\\Database\\Type\\DateType' => $vendorDir . '/cakephp/database/Type/DateType.php',
|
||||
'Cake\\Database\\Type\\DecimalType' => $vendorDir . '/cakephp/database/Type/DecimalType.php',
|
||||
'Cake\\Database\\Type\\EnumLabelInterface' => $vendorDir . '/cakephp/database/Type/EnumLabelInterface.php',
|
||||
'Cake\\Database\\Type\\EnumType' => $vendorDir . '/cakephp/database/Type/EnumType.php',
|
||||
'Cake\\Database\\Type\\ExpressionTypeCasterTrait' => $vendorDir . '/cakephp/database/Type/ExpressionTypeCasterTrait.php',
|
||||
'Cake\\Database\\Type\\ExpressionTypeInterface' => $vendorDir . '/cakephp/database/Type/ExpressionTypeInterface.php',
|
||||
'Cake\\Database\\Type\\FloatType' => $vendorDir . '/cakephp/database/Type/FloatType.php',
|
||||
'Cake\\Database\\Type\\IntegerType' => $vendorDir . '/cakephp/database/Type/IntegerType.php',
|
||||
'Cake\\Database\\Type\\JsonType' => $vendorDir . '/cakephp/database/Type/JsonType.php',
|
||||
'Cake\\Database\\Type\\OptionalConvertInterface' => $vendorDir . '/cakephp/database/Type/OptionalConvertInterface.php',
|
||||
'Cake\\Database\\Type\\StringType' => $vendorDir . '/cakephp/database/Type/StringType.php',
|
||||
'Cake\\Database\\Type\\TimeType' => $vendorDir . '/cakephp/database/Type/TimeType.php',
|
||||
'Cake\\Database\\Type\\UuidType' => $vendorDir . '/cakephp/database/Type/UuidType.php',
|
||||
'Cake\\Database\\TypedResultInterface' => $vendorDir . '/cakephp/database/TypedResultInterface.php',
|
||||
'Cake\\Database\\TypedResultTrait' => $vendorDir . '/cakephp/database/TypedResultTrait.php',
|
||||
'Cake\\Database\\ValueBinder' => $vendorDir . '/cakephp/database/ValueBinder.php',
|
||||
'Cake\\Datasource\\ConnectionInterface' => $vendorDir . '/cakephp/datasource/ConnectionInterface.php',
|
||||
'Cake\\Datasource\\ConnectionManager' => $vendorDir . '/cakephp/datasource/ConnectionManager.php',
|
||||
'Cake\\Datasource\\ConnectionRegistry' => $vendorDir . '/cakephp/datasource/ConnectionRegistry.php',
|
||||
'Cake\\Datasource\\EntityInterface' => $vendorDir . '/cakephp/datasource/EntityInterface.php',
|
||||
'Cake\\Datasource\\EntityTrait' => $vendorDir . '/cakephp/datasource/EntityTrait.php',
|
||||
'Cake\\Datasource\\Exception\\InvalidPrimaryKeyException' => $vendorDir . '/cakephp/datasource/Exception/InvalidPrimaryKeyException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingDatasourceConfigException' => $vendorDir . '/cakephp/datasource/Exception/MissingDatasourceConfigException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingDatasourceException' => $vendorDir . '/cakephp/datasource/Exception/MissingDatasourceException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingModelException' => $vendorDir . '/cakephp/datasource/Exception/MissingModelException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingPropertyException' => $vendorDir . '/cakephp/datasource/Exception/MissingPropertyException.php',
|
||||
'Cake\\Datasource\\Exception\\RecordNotFoundException' => $vendorDir . '/cakephp/datasource/Exception/RecordNotFoundException.php',
|
||||
'Cake\\Datasource\\FactoryLocator' => $vendorDir . '/cakephp/datasource/FactoryLocator.php',
|
||||
'Cake\\Datasource\\FixtureInterface' => $vendorDir . '/cakephp/datasource/FixtureInterface.php',
|
||||
'Cake\\Datasource\\InvalidPropertyInterface' => $vendorDir . '/cakephp/datasource/InvalidPropertyInterface.php',
|
||||
'Cake\\Datasource\\Locator\\AbstractLocator' => $vendorDir . '/cakephp/datasource/Locator/AbstractLocator.php',
|
||||
'Cake\\Datasource\\Locator\\LocatorInterface' => $vendorDir . '/cakephp/datasource/Locator/LocatorInterface.php',
|
||||
'Cake\\Datasource\\ModelAwareTrait' => $vendorDir . '/cakephp/datasource/ModelAwareTrait.php',
|
||||
'Cake\\Datasource\\Paging\\Exception\\PageOutOfBoundsException' => $vendorDir . '/cakephp/datasource/Paging/Exception/PageOutOfBoundsException.php',
|
||||
'Cake\\Datasource\\Paging\\NumericPaginator' => $vendorDir . '/cakephp/datasource/Paging/NumericPaginator.php',
|
||||
'Cake\\Datasource\\Paging\\PaginatedInterface' => $vendorDir . '/cakephp/datasource/Paging/PaginatedInterface.php',
|
||||
'Cake\\Datasource\\Paging\\PaginatedResultSet' => $vendorDir . '/cakephp/datasource/Paging/PaginatedResultSet.php',
|
||||
'Cake\\Datasource\\Paging\\PaginatorInterface' => $vendorDir . '/cakephp/datasource/Paging/PaginatorInterface.php',
|
||||
'Cake\\Datasource\\Paging\\SimplePaginator' => $vendorDir . '/cakephp/datasource/Paging/SimplePaginator.php',
|
||||
'Cake\\Datasource\\Paging\\SortField' => $vendorDir . '/cakephp/datasource/Paging/SortField.php',
|
||||
'Cake\\Datasource\\Paging\\SortableFieldsBuilder' => $vendorDir . '/cakephp/datasource/Paging/SortableFieldsBuilder.php',
|
||||
'Cake\\Datasource\\QueryCacher' => $vendorDir . '/cakephp/datasource/QueryCacher.php',
|
||||
'Cake\\Datasource\\QueryInterface' => $vendorDir . '/cakephp/datasource/QueryInterface.php',
|
||||
'Cake\\Datasource\\RepositoryInterface' => $vendorDir . '/cakephp/datasource/RepositoryInterface.php',
|
||||
'Cake\\Datasource\\ResultSetDecorator' => $vendorDir . '/cakephp/datasource/ResultSetDecorator.php',
|
||||
'Cake\\Datasource\\ResultSetInterface' => $vendorDir . '/cakephp/datasource/ResultSetInterface.php',
|
||||
'Cake\\Datasource\\RuleInvoker' => $vendorDir . '/cakephp/datasource/RuleInvoker.php',
|
||||
'Cake\\Datasource\\RulesAwareTrait' => $vendorDir . '/cakephp/datasource/RulesAwareTrait.php',
|
||||
'Cake\\Datasource\\RulesChecker' => $vendorDir . '/cakephp/datasource/RulesChecker.php',
|
||||
'Cake\\Datasource\\SchemaInterface' => $vendorDir . '/cakephp/datasource/SchemaInterface.php',
|
||||
'Cake\\Utility\\CookieCryptTrait' => $vendorDir . '/cakephp/utility/CookieCryptTrait.php',
|
||||
'Cake\\Utility\\Crypto\\OpenSsl' => $vendorDir . '/cakephp/utility/Crypto/OpenSsl.php',
|
||||
'Cake\\Utility\\Exception\\XmlException' => $vendorDir . '/cakephp/utility/Exception/XmlException.php',
|
||||
'Cake\\Utility\\Filesystem' => $vendorDir . '/cakephp/utility/Filesystem.php',
|
||||
'Cake\\Utility\\Hash' => $vendorDir . '/cakephp/utility/Hash.php',
|
||||
'Cake\\Utility\\Inflector' => $vendorDir . '/cakephp/utility/Inflector.php',
|
||||
'Cake\\Utility\\MergeVariablesTrait' => $vendorDir . '/cakephp/utility/MergeVariablesTrait.php',
|
||||
'Cake\\Utility\\Security' => $vendorDir . '/cakephp/utility/Security.php',
|
||||
'Cake\\Utility\\Text' => $vendorDir . '/cakephp/utility/Text.php',
|
||||
'Cake\\Utility\\Xml' => $vendorDir . '/cakephp/utility/Xml.php',
|
||||
'Carbon\\AbstractTranslator' => $vendorDir . '/nesbot/carbon/src/Carbon/AbstractTranslator.php',
|
||||
'Carbon\\Callback' => $vendorDir . '/nesbot/carbon/src/Carbon/Callback.php',
|
||||
'Carbon\\Carbon' => $vendorDir . '/nesbot/carbon/src/Carbon/Carbon.php',
|
||||
@@ -3413,6 +3603,55 @@ return array(
|
||||
'League\\Config\\MutableConfigurationInterface' => $vendorDir . '/league/config/src/MutableConfigurationInterface.php',
|
||||
'League\\Config\\ReadOnlyConfiguration' => $vendorDir . '/league/config/src/ReadOnlyConfiguration.php',
|
||||
'League\\Config\\SchemaBuilderInterface' => $vendorDir . '/league/config/src/SchemaBuilderInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentInterface' => $vendorDir . '/league/container/src/Argument/ArgumentInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentReflectorInterface' => $vendorDir . '/league/container/src/Argument/ArgumentReflectorInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentReflectorTrait' => $vendorDir . '/league/container/src/Argument/ArgumentReflectorTrait.php',
|
||||
'League\\Container\\Argument\\ArgumentResolverInterface' => $vendorDir . '/league/container/src/Argument/ArgumentResolverInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentResolverTrait' => $vendorDir . '/league/container/src/Argument/ArgumentResolverTrait.php',
|
||||
'League\\Container\\Argument\\DefaultValueArgument' => $vendorDir . '/league/container/src/Argument/DefaultValueArgument.php',
|
||||
'League\\Container\\Argument\\DefaultValueInterface' => $vendorDir . '/league/container/src/Argument/DefaultValueInterface.php',
|
||||
'League\\Container\\Argument\\LiteralArgument' => $vendorDir . '/league/container/src/Argument/LiteralArgument.php',
|
||||
'League\\Container\\Argument\\LiteralArgumentInterface' => $vendorDir . '/league/container/src/Argument/LiteralArgumentInterface.php',
|
||||
'League\\Container\\Argument\\Literal\\ArrayArgument' => $vendorDir . '/league/container/src/Argument/Literal/ArrayArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\BooleanArgument' => $vendorDir . '/league/container/src/Argument/Literal/BooleanArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\CallableArgument' => $vendorDir . '/league/container/src/Argument/Literal/CallableArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\FloatArgument' => $vendorDir . '/league/container/src/Argument/Literal/FloatArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\IntegerArgument' => $vendorDir . '/league/container/src/Argument/Literal/IntegerArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\ObjectArgument' => $vendorDir . '/league/container/src/Argument/Literal/ObjectArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\StringArgument' => $vendorDir . '/league/container/src/Argument/Literal/StringArgument.php',
|
||||
'League\\Container\\Argument\\ResolvableArgument' => $vendorDir . '/league/container/src/Argument/ResolvableArgument.php',
|
||||
'League\\Container\\Argument\\ResolvableArgumentInterface' => $vendorDir . '/league/container/src/Argument/ResolvableArgumentInterface.php',
|
||||
'League\\Container\\Attribute\\AttributeInterface' => $vendorDir . '/league/container/src/Attribute/AttributeInterface.php',
|
||||
'League\\Container\\Attribute\\Inject' => $vendorDir . '/league/container/src/Attribute/Inject.php',
|
||||
'League\\Container\\Attribute\\Resolve' => $vendorDir . '/league/container/src/Attribute/Resolve.php',
|
||||
'League\\Container\\Container' => $vendorDir . '/league/container/src/Container.php',
|
||||
'League\\Container\\ContainerAwareInterface' => $vendorDir . '/league/container/src/ContainerAwareInterface.php',
|
||||
'League\\Container\\ContainerAwareTrait' => $vendorDir . '/league/container/src/ContainerAwareTrait.php',
|
||||
'League\\Container\\DefinitionContainerInterface' => $vendorDir . '/league/container/src/DefinitionContainerInterface.php',
|
||||
'League\\Container\\Definition\\Definition' => $vendorDir . '/league/container/src/Definition/Definition.php',
|
||||
'League\\Container\\Definition\\DefinitionAggregate' => $vendorDir . '/league/container/src/Definition/DefinitionAggregate.php',
|
||||
'League\\Container\\Definition\\DefinitionAggregateInterface' => $vendorDir . '/league/container/src/Definition/DefinitionAggregateInterface.php',
|
||||
'League\\Container\\Definition\\DefinitionInterface' => $vendorDir . '/league/container/src/Definition/DefinitionInterface.php',
|
||||
'League\\Container\\Event\\BeforeResolveEvent' => $vendorDir . '/league/container/src/Event/BeforeResolveEvent.php',
|
||||
'League\\Container\\Event\\ContainerEvent' => $vendorDir . '/league/container/src/Event/ContainerEvent.php',
|
||||
'League\\Container\\Event\\DefinitionResolvedEvent' => $vendorDir . '/league/container/src/Event/DefinitionResolvedEvent.php',
|
||||
'League\\Container\\Event\\EventAwareTrait' => $vendorDir . '/league/container/src/Event/EventAwareTrait.php',
|
||||
'League\\Container\\Event\\EventDispatcher' => $vendorDir . '/league/container/src/Event/EventDispatcher.php',
|
||||
'League\\Container\\Event\\EventFilter' => $vendorDir . '/league/container/src/Event/EventFilter.php',
|
||||
'League\\Container\\Event\\OnDefineEvent' => $vendorDir . '/league/container/src/Event/OnDefineEvent.php',
|
||||
'League\\Container\\Event\\ServiceResolvedEvent' => $vendorDir . '/league/container/src/Event/ServiceResolvedEvent.php',
|
||||
'League\\Container\\Exception\\ContainerException' => $vendorDir . '/league/container/src/Exception/ContainerException.php',
|
||||
'League\\Container\\Exception\\NotFoundException' => $vendorDir . '/league/container/src/Exception/NotFoundException.php',
|
||||
'League\\Container\\Inflector\\Inflector' => $vendorDir . '/league/container/src/Inflector/Inflector.php',
|
||||
'League\\Container\\Inflector\\InflectorAggregate' => $vendorDir . '/league/container/src/Inflector/InflectorAggregate.php',
|
||||
'League\\Container\\Inflector\\InflectorAggregateInterface' => $vendorDir . '/league/container/src/Inflector/InflectorAggregateInterface.php',
|
||||
'League\\Container\\Inflector\\InflectorInterface' => $vendorDir . '/league/container/src/Inflector/InflectorInterface.php',
|
||||
'League\\Container\\ReflectionContainer' => $vendorDir . '/league/container/src/ReflectionContainer.php',
|
||||
'League\\Container\\ServiceProvider\\AbstractServiceProvider' => $vendorDir . '/league/container/src/ServiceProvider/AbstractServiceProvider.php',
|
||||
'League\\Container\\ServiceProvider\\BootableServiceProviderInterface' => $vendorDir . '/league/container/src/ServiceProvider/BootableServiceProviderInterface.php',
|
||||
'League\\Container\\ServiceProvider\\ServiceProviderAggregate' => $vendorDir . '/league/container/src/ServiceProvider/ServiceProviderAggregate.php',
|
||||
'League\\Container\\ServiceProvider\\ServiceProviderAggregateInterface' => $vendorDir . '/league/container/src/ServiceProvider/ServiceProviderAggregateInterface.php',
|
||||
'League\\Container\\ServiceProvider\\ServiceProviderInterface' => $vendorDir . '/league/container/src/ServiceProvider/ServiceProviderInterface.php',
|
||||
'League\\Flysystem\\CalculateChecksumFromStream' => $vendorDir . '/league/flysystem/src/CalculateChecksumFromStream.php',
|
||||
'League\\Flysystem\\ChecksumAlgoIsNotSupported' => $vendorDir . '/league/flysystem/src/ChecksumAlgoIsNotSupported.php',
|
||||
'League\\Flysystem\\ChecksumProvider' => $vendorDir . '/league/flysystem/src/ChecksumProvider.php',
|
||||
@@ -4921,6 +5160,76 @@ return array(
|
||||
'PharIo\\Version\\VersionConstraintParser' => $vendorDir . '/phar-io/version/src/VersionConstraintParser.php',
|
||||
'PharIo\\Version\\VersionConstraintValue' => $vendorDir . '/phar-io/version/src/VersionConstraintValue.php',
|
||||
'PharIo\\Version\\VersionNumber' => $vendorDir . '/phar-io/version/src/VersionNumber.php',
|
||||
'Phinx\\Config\\Config' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/Config.php',
|
||||
'Phinx\\Config\\ConfigInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php',
|
||||
'Phinx\\Config\\FeatureFlags' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/FeatureFlags.php',
|
||||
'Phinx\\Config\\NamespaceAwareInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareInterface.php',
|
||||
'Phinx\\Config\\NamespaceAwareTrait' => $vendorDir . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareTrait.php',
|
||||
'Phinx\\Console\\Command\\AbstractCommand' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php',
|
||||
'Phinx\\Console\\Command\\Breakpoint' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php',
|
||||
'Phinx\\Console\\Command\\Create' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Create.php',
|
||||
'Phinx\\Console\\Command\\Init' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Init.php',
|
||||
'Phinx\\Console\\Command\\ListAliases' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/ListAliases.php',
|
||||
'Phinx\\Console\\Command\\Migrate' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php',
|
||||
'Phinx\\Console\\Command\\Rollback' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php',
|
||||
'Phinx\\Console\\Command\\SeedCreate' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php',
|
||||
'Phinx\\Console\\Command\\SeedRun' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php',
|
||||
'Phinx\\Console\\Command\\Status' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Status.php',
|
||||
'Phinx\\Console\\Command\\Test' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/Command/Test.php',
|
||||
'Phinx\\Console\\PhinxApplication' => $vendorDir . '/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php',
|
||||
'Phinx\\Db\\Action\\Action' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/Action.php',
|
||||
'Phinx\\Db\\Action\\AddColumn' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/AddColumn.php',
|
||||
'Phinx\\Db\\Action\\AddForeignKey' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/AddForeignKey.php',
|
||||
'Phinx\\Db\\Action\\AddIndex' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/AddIndex.php',
|
||||
'Phinx\\Db\\Action\\ChangeColumn' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/ChangeColumn.php',
|
||||
'Phinx\\Db\\Action\\ChangeComment' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/ChangeComment.php',
|
||||
'Phinx\\Db\\Action\\ChangePrimaryKey' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/ChangePrimaryKey.php',
|
||||
'Phinx\\Db\\Action\\CreateTable' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/CreateTable.php',
|
||||
'Phinx\\Db\\Action\\DropForeignKey' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/DropForeignKey.php',
|
||||
'Phinx\\Db\\Action\\DropIndex' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/DropIndex.php',
|
||||
'Phinx\\Db\\Action\\DropTable' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/DropTable.php',
|
||||
'Phinx\\Db\\Action\\RemoveColumn' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/RemoveColumn.php',
|
||||
'Phinx\\Db\\Action\\RenameColumn' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/RenameColumn.php',
|
||||
'Phinx\\Db\\Action\\RenameTable' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Action/RenameTable.php',
|
||||
'Phinx\\Db\\Adapter\\AbstractAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\AdapterFactory' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php',
|
||||
'Phinx\\Db\\Adapter\\AdapterInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php',
|
||||
'Phinx\\Db\\Adapter\\AdapterWrapper' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php',
|
||||
'Phinx\\Db\\Adapter\\DirectActionInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/DirectActionInterface.php',
|
||||
'Phinx\\Db\\Adapter\\MysqlAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\PdoAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\PostgresAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\ProxyAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\SQLiteAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\SqlServerAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\TablePrefixAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\TimedOutputAdapter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\UnsupportedColumnTypeException' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/UnsupportedColumnTypeException.php',
|
||||
'Phinx\\Db\\Adapter\\WrapperInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php',
|
||||
'Phinx\\Db\\Plan\\AlterTable' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Plan/AlterTable.php',
|
||||
'Phinx\\Db\\Plan\\Intent' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Plan/Intent.php',
|
||||
'Phinx\\Db\\Plan\\NewTable' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Plan/NewTable.php',
|
||||
'Phinx\\Db\\Plan\\Plan' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Plan/Plan.php',
|
||||
'Phinx\\Db\\Plan\\Solver\\ActionSplitter' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Plan/Solver/ActionSplitter.php',
|
||||
'Phinx\\Db\\Table' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table.php',
|
||||
'Phinx\\Db\\Table\\Column' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/Column.php',
|
||||
'Phinx\\Db\\Table\\ForeignKey' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php',
|
||||
'Phinx\\Db\\Table\\Index' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/Index.php',
|
||||
'Phinx\\Db\\Table\\Table' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Table/Table.php',
|
||||
'Phinx\\Db\\Util\\AlterInstructions' => $vendorDir . '/robmorgan/phinx/src/Phinx/Db/Util/AlterInstructions.php',
|
||||
'Phinx\\Migration\\AbstractMigration' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php',
|
||||
'Phinx\\Migration\\AbstractTemplateCreation' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php',
|
||||
'Phinx\\Migration\\CreationInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php',
|
||||
'Phinx\\Migration\\IrreversibleMigrationException' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php',
|
||||
'Phinx\\Migration\\Manager' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/Manager.php',
|
||||
'Phinx\\Migration\\Manager\\Environment' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php',
|
||||
'Phinx\\Migration\\MigrationInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Migration/MigrationInterface.php',
|
||||
'Phinx\\Seed\\AbstractSeed' => $vendorDir . '/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php',
|
||||
'Phinx\\Seed\\SeedInterface' => $vendorDir . '/robmorgan/phinx/src/Phinx/Seed/SeedInterface.php',
|
||||
'Phinx\\Util\\Expression' => $vendorDir . '/robmorgan/phinx/src/Phinx/Util/Expression.php',
|
||||
'Phinx\\Util\\Literal' => $vendorDir . '/robmorgan/phinx/src/Phinx/Util/Literal.php',
|
||||
'Phinx\\Util\\Util' => $vendorDir . '/robmorgan/phinx/src/Phinx/Util/Util.php',
|
||||
'Phinx\\Wrapper\\TextWrapper' => $vendorDir . '/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php',
|
||||
'PhpOffice\\PhpSpreadsheet\\Calculation\\ArrayEnabled' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php',
|
||||
'PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php',
|
||||
'PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation' => $vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php',
|
||||
@@ -6661,6 +6970,96 @@ return array(
|
||||
'Symfony\\Component\\Clock\\MonotonicClock' => $vendorDir . '/symfony/clock/MonotonicClock.php',
|
||||
'Symfony\\Component\\Clock\\NativeClock' => $vendorDir . '/symfony/clock/NativeClock.php',
|
||||
'Symfony\\Component\\Clock\\Test\\ClockSensitiveTrait' => $vendorDir . '/symfony/clock/Test/ClockSensitiveTrait.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ClassBuilder' => $vendorDir . '/symfony/config/Builder/ClassBuilder.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ConfigBuilderGenerator' => $vendorDir . '/symfony/config/Builder/ConfigBuilderGenerator.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ConfigBuilderGeneratorInterface' => $vendorDir . '/symfony/config/Builder/ConfigBuilderGeneratorInterface.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ConfigBuilderInterface' => $vendorDir . '/symfony/config/Builder/ConfigBuilderInterface.php',
|
||||
'Symfony\\Component\\Config\\Builder\\Method' => $vendorDir . '/symfony/config/Builder/Method.php',
|
||||
'Symfony\\Component\\Config\\Builder\\Property' => $vendorDir . '/symfony/config/Builder/Property.php',
|
||||
'Symfony\\Component\\Config\\ConfigCache' => $vendorDir . '/symfony/config/ConfigCache.php',
|
||||
'Symfony\\Component\\Config\\ConfigCacheFactory' => $vendorDir . '/symfony/config/ConfigCacheFactory.php',
|
||||
'Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => $vendorDir . '/symfony/config/ConfigCacheFactoryInterface.php',
|
||||
'Symfony\\Component\\Config\\ConfigCacheInterface' => $vendorDir . '/symfony/config/ConfigCacheInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ArrayNode' => $vendorDir . '/symfony/config/Definition/ArrayNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ArrayShapeGenerator' => $vendorDir . '/symfony/config/Definition/ArrayShapeGenerator.php',
|
||||
'Symfony\\Component\\Config\\Definition\\BaseNode' => $vendorDir . '/symfony/config/Definition/BaseNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\BooleanNode' => $vendorDir . '/symfony/config/Definition/BooleanNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\BuilderAwareInterface' => $vendorDir . '/symfony/config/Definition/Builder/BuilderAwareInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/EnumNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ExprBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/FloatNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\IntegerNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/IntegerNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\MergeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/MergeBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NodeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/NodeBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/NodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface' => $vendorDir . '/symfony/config/Definition/Builder/NodeParentInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NormalizationBuilder' => $vendorDir . '/symfony/config/Definition/Builder/NormalizationBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NumericNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/NumericNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ParentNodeDefinitionInterface' => $vendorDir . '/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ScalarNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/ScalarNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\StringNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/StringNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder' => $vendorDir . '/symfony/config/Definition/Builder/TreeBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ValidationBuilder' => $vendorDir . '/symfony/config/Definition/Builder/ValidationBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\VariableNodeDefinition' => $vendorDir . '/symfony/config/Definition/Builder/VariableNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ConfigurableInterface' => $vendorDir . '/symfony/config/Definition/ConfigurableInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Configuration' => $vendorDir . '/symfony/config/Definition/Configuration.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ConfigurationInterface' => $vendorDir . '/symfony/config/Definition/ConfigurationInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Configurator\\DefinitionConfigurator' => $vendorDir . '/symfony/config/Definition/Configurator/DefinitionConfigurator.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Dumper\\XmlReferenceDumper' => $vendorDir . '/symfony/config/Definition/Dumper/XmlReferenceDumper.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Dumper\\YamlReferenceDumper' => $vendorDir . '/symfony/config/Definition/Dumper/YamlReferenceDumper.php',
|
||||
'Symfony\\Component\\Config\\Definition\\EnumNode' => $vendorDir . '/symfony/config/Definition/EnumNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\DuplicateKeyException' => $vendorDir . '/symfony/config/Definition/Exception/DuplicateKeyException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\Exception' => $vendorDir . '/symfony/config/Definition/Exception/Exception.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\ForbiddenOverwriteException' => $vendorDir . '/symfony/config/Definition/Exception/ForbiddenOverwriteException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidConfigurationException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidDefinitionException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => $vendorDir . '/symfony/config/Definition/Exception/InvalidTypeException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => $vendorDir . '/symfony/config/Definition/Exception/UnsetKeyException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\FloatNode' => $vendorDir . '/symfony/config/Definition/FloatNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\IntegerNode' => $vendorDir . '/symfony/config/Definition/IntegerNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Loader\\DefinitionFileLoader' => $vendorDir . '/symfony/config/Definition/Loader/DefinitionFileLoader.php',
|
||||
'Symfony\\Component\\Config\\Definition\\NodeInterface' => $vendorDir . '/symfony/config/Definition/NodeInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\NumericNode' => $vendorDir . '/symfony/config/Definition/NumericNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Processor' => $vendorDir . '/symfony/config/Definition/Processor.php',
|
||||
'Symfony\\Component\\Config\\Definition\\PrototypeNodeInterface' => $vendorDir . '/symfony/config/Definition/PrototypeNodeInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => $vendorDir . '/symfony/config/Definition/PrototypedArrayNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ScalarNode' => $vendorDir . '/symfony/config/Definition/ScalarNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\StringNode' => $vendorDir . '/symfony/config/Definition/StringNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\VariableNode' => $vendorDir . '/symfony/config/Definition/VariableNode.php',
|
||||
'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => $vendorDir . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php',
|
||||
'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => $vendorDir . '/symfony/config/Exception/FileLocatorFileNotFoundException.php',
|
||||
'Symfony\\Component\\Config\\Exception\\LoaderLoadException' => $vendorDir . '/symfony/config/Exception/LoaderLoadException.php',
|
||||
'Symfony\\Component\\Config\\Exception\\LogicException' => $vendorDir . '/symfony/config/Exception/LogicException.php',
|
||||
'Symfony\\Component\\Config\\FileLocator' => $vendorDir . '/symfony/config/FileLocator.php',
|
||||
'Symfony\\Component\\Config\\FileLocatorInterface' => $vendorDir . '/symfony/config/FileLocatorInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => $vendorDir . '/symfony/config/Loader/DelegatingLoader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\DirectoryAwareLoaderInterface' => $vendorDir . '/symfony/config/Loader/DirectoryAwareLoaderInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\FileLoader' => $vendorDir . '/symfony/config/Loader/FileLoader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\GlobFileLoader' => $vendorDir . '/symfony/config/Loader/GlobFileLoader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\Loader' => $vendorDir . '/symfony/config/Loader/Loader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\LoaderInterface' => $vendorDir . '/symfony/config/Loader/LoaderInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\LoaderResolver' => $vendorDir . '/symfony/config/Loader/LoaderResolver.php',
|
||||
'Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => $vendorDir . '/symfony/config/Loader/LoaderResolverInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\ParamConfigurator' => $vendorDir . '/symfony/config/Loader/ParamConfigurator.php',
|
||||
'Symfony\\Component\\Config\\ResourceCheckerConfigCache' => $vendorDir . '/symfony/config/ResourceCheckerConfigCache.php',
|
||||
'Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => $vendorDir . '/symfony/config/ResourceCheckerConfigCacheFactory.php',
|
||||
'Symfony\\Component\\Config\\ResourceCheckerInterface' => $vendorDir . '/symfony/config/ResourceCheckerInterface.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ClassExistenceResource' => $vendorDir . '/symfony/config/Resource/ClassExistenceResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ComposerResource' => $vendorDir . '/symfony/config/Resource/ComposerResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\DirectoryResource' => $vendorDir . '/symfony/config/Resource/DirectoryResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\FileExistenceResource' => $vendorDir . '/symfony/config/Resource/FileExistenceResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\FileResource' => $vendorDir . '/symfony/config/Resource/FileResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\GlobResource' => $vendorDir . '/symfony/config/Resource/GlobResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => $vendorDir . '/symfony/config/Resource/ReflectionClassResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ResourceInterface' => $vendorDir . '/symfony/config/Resource/ResourceInterface.php',
|
||||
'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceChecker.php',
|
||||
'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => $vendorDir . '/symfony/config/Resource/SelfCheckingResourceInterface.php',
|
||||
'Symfony\\Component\\Config\\Resource\\SkippingResourceChecker' => $vendorDir . '/symfony/config/Resource/SkippingResourceChecker.php',
|
||||
'Symfony\\Component\\Config\\Util\\Exception\\InvalidXmlException' => $vendorDir . '/symfony/config/Util/Exception/InvalidXmlException.php',
|
||||
'Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => $vendorDir . '/symfony/config/Util/Exception/XmlParsingException.php',
|
||||
'Symfony\\Component\\Config\\Util\\XmlUtils' => $vendorDir . '/symfony/config/Util/XmlUtils.php',
|
||||
'Symfony\\Component\\Console\\Application' => $vendorDir . '/symfony/console/Application.php',
|
||||
'Symfony\\Component\\Console\\Attribute\\Argument' => $vendorDir . '/symfony/console/Attribute/Argument.php',
|
||||
'Symfony\\Component\\Console\\Attribute\\AsCommand' => $vendorDir . '/symfony/console/Attribute/AsCommand.php',
|
||||
@@ -6869,6 +7268,14 @@ return array(
|
||||
'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => $vendorDir . '/symfony/event-dispatcher/EventSubscriberInterface.php',
|
||||
'Symfony\\Component\\EventDispatcher\\GenericEvent' => $vendorDir . '/symfony/event-dispatcher/GenericEvent.php',
|
||||
'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => $vendorDir . '/symfony/event-dispatcher/ImmutableEventDispatcher.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/ExceptionInterface.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => $vendorDir . '/symfony/filesystem/Exception/FileNotFoundException.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\IOException' => $vendorDir . '/symfony/filesystem/Exception/IOException.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => $vendorDir . '/symfony/filesystem/Exception/IOExceptionInterface.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => $vendorDir . '/symfony/filesystem/Exception/InvalidArgumentException.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => $vendorDir . '/symfony/filesystem/Exception/RuntimeException.php',
|
||||
'Symfony\\Component\\Filesystem\\Filesystem' => $vendorDir . '/symfony/filesystem/Filesystem.php',
|
||||
'Symfony\\Component\\Filesystem\\Path' => $vendorDir . '/symfony/filesystem/Path.php',
|
||||
'Symfony\\Component\\Finder\\Comparator\\Comparator' => $vendorDir . '/symfony/finder/Comparator/Comparator.php',
|
||||
'Symfony\\Component\\Finder\\Comparator\\DateComparator' => $vendorDir . '/symfony/finder/Comparator/DateComparator.php',
|
||||
'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => $vendorDir . '/symfony/finder/Comparator/NumberComparator.php',
|
||||
|
||||
Vendored
+3
-1
@@ -11,14 +11,15 @@ return array(
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => $vendorDir . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => $vendorDir . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => $vendorDir . '/symfony/string/Resources/functions.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => $vendorDir . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => $vendorDir . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'72142d7b40a3a0b14e91825290b5ad82' => $vendorDir . '/cakephp/core/functions.php',
|
||||
'35a6ad97d21e794e7e22a17d806652e4' => $vendorDir . '/nunomaduro/termwind/src/Functions.php',
|
||||
'2203a247e6fda86070a5e4e07aed533a' => $vendorDir . '/symfony/clock/Resources/now.php',
|
||||
'09f6b20656683369174dd6fa83b7e5fb' => $vendorDir . '/symfony/polyfill-uuid/bootstrap.php',
|
||||
@@ -33,6 +34,7 @@ return array(
|
||||
'58571171fd5812e6e447dce228f52f4d' => $vendorDir . '/laravel/framework/src/Illuminate/Support/helpers.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => $vendorDir . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'3bd81c9b8fcc150b69d8b63b4d2ccf23' => $vendorDir . '/spatie/flare-client-php/src/helpers.php',
|
||||
'948ad5488880985ff1c06721a4e447fe' => $vendorDir . '/cakephp/utility/bootstrap.php',
|
||||
'ec07570ca5a812141189b1fa81503674' => $vendorDir . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
|
||||
'801c31d8ed748cfa537fa45402288c95' => $vendorDir . '/psy/psysh/src/functions.php',
|
||||
'b84a5ba6d9a8ee8cf648b71c42c799b7' => $vendorDir . '/akaunting/laravel-setting/src/helpers.php',
|
||||
|
||||
Vendored
+12
-3
@@ -8,7 +8,7 @@ $baseDir = dirname($vendorDir);
|
||||
return array(
|
||||
'voku\\' => array($vendorDir . '/voku/portable-ascii/src/voku'),
|
||||
'phpseclib3\\' => array($vendorDir . '/phpseclib/phpseclib/phpseclib'),
|
||||
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/reflection-common/src', $vendorDir . '/phpdocumentor/type-resolver/src'),
|
||||
'phpDocumentor\\Reflection\\' => array($vendorDir . '/phpdocumentor/type-resolver/src', $vendorDir . '/phpdocumentor/reflection-common/src'),
|
||||
'ZipStream\\' => array($vendorDir . '/maennchen/zipstream-php/src'),
|
||||
'Whoops\\' => array($vendorDir . '/filp/whoops/src/Whoops'),
|
||||
'Webmozart\\Assert\\' => array($vendorDir . '/webmozart/assert/src'),
|
||||
@@ -47,16 +47,18 @@ return array(
|
||||
'Symfony\\Component\\HttpKernel\\' => array($vendorDir . '/symfony/http-kernel'),
|
||||
'Symfony\\Component\\HttpFoundation\\' => array($vendorDir . '/symfony/http-foundation'),
|
||||
'Symfony\\Component\\Finder\\' => array($vendorDir . '/symfony/finder'),
|
||||
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),
|
||||
'Symfony\\Component\\EventDispatcher\\' => array($vendorDir . '/symfony/event-dispatcher'),
|
||||
'Symfony\\Component\\ErrorHandler\\' => array($vendorDir . '/symfony/error-handler'),
|
||||
'Symfony\\Component\\CssSelector\\' => array($vendorDir . '/symfony/css-selector'),
|
||||
'Symfony\\Component\\Console\\' => array($vendorDir . '/symfony/console'),
|
||||
'Symfony\\Component\\Config\\' => array($vendorDir . '/symfony/config'),
|
||||
'Symfony\\Component\\Clock\\' => array($vendorDir . '/symfony/clock'),
|
||||
'Spatie\\QueryBuilder\\Database\\Factories\\' => array($vendorDir . '/spatie/laravel-query-builder/database/factories'),
|
||||
'Spatie\\QueryBuilder\\' => array($vendorDir . '/spatie/laravel-query-builder/src'),
|
||||
'Spatie\\LaravelPackageTools\\' => array($vendorDir . '/spatie/laravel-package-tools/src'),
|
||||
'Spatie\\LaravelIgnition\\' => array($vendorDir . '/spatie/error-solutions/legacy/laravel-ignition', $vendorDir . '/spatie/laravel-ignition/src'),
|
||||
'Spatie\\Ignition\\' => array($vendorDir . '/spatie/error-solutions/legacy/ignition', $vendorDir . '/spatie/ignition/src'),
|
||||
'Spatie\\LaravelIgnition\\' => array($vendorDir . '/spatie/laravel-ignition/src', $vendorDir . '/spatie/error-solutions/legacy/laravel-ignition'),
|
||||
'Spatie\\Ignition\\' => array($vendorDir . '/spatie/ignition/src', $vendorDir . '/spatie/error-solutions/legacy/ignition'),
|
||||
'Spatie\\FlareClient\\' => array($vendorDir . '/spatie/flare-client-php/src'),
|
||||
'Spatie\\ErrorSolutions\\' => array($vendorDir . '/spatie/error-solutions/src'),
|
||||
'Spatie\\Backtrace\\' => array($vendorDir . '/spatie/backtrace/src'),
|
||||
@@ -77,6 +79,7 @@ return array(
|
||||
'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'),
|
||||
'PhpOption\\' => array($vendorDir . '/phpoption/phpoption/src/PhpOption'),
|
||||
'PhpOffice\\PhpSpreadsheet\\' => array($vendorDir . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet'),
|
||||
'Phinx\\' => array($vendorDir . '/robmorgan/phinx/src/Phinx'),
|
||||
'ParagonIE\\ConstantTime\\' => array($vendorDir . '/paragonie/constant_time_encoding/src'),
|
||||
'ParaTest\\' => array($vendorDir . '/brianium/paratest/src'),
|
||||
'PHPStan\\PhpDocParser\\' => array($vendorDir . '/phpstan/phpdoc-parser/src'),
|
||||
@@ -89,6 +92,7 @@ return array(
|
||||
'League\\MimeTypeDetection\\' => array($vendorDir . '/league/mime-type-detection/src'),
|
||||
'League\\Flysystem\\Local\\' => array($vendorDir . '/league/flysystem-local'),
|
||||
'League\\Flysystem\\' => array($vendorDir . '/league/flysystem/src'),
|
||||
'League\\Container\\' => array($vendorDir . '/league/container/src'),
|
||||
'League\\Config\\' => array($vendorDir . '/league/config/src'),
|
||||
'League\\CommonMark\\' => array($vendorDir . '/league/commonmark/src'),
|
||||
'Laravel\\Ui\\' => array($vendorDir . '/laravel/ui/src'),
|
||||
@@ -134,6 +138,11 @@ return array(
|
||||
'Complex\\' => array($vendorDir . '/markbaker/complex/classes/src'),
|
||||
'Carbon\\Doctrine\\' => array($vendorDir . '/carbonphp/carbon-doctrine-types/src/Carbon/Doctrine'),
|
||||
'Carbon\\' => array($vendorDir . '/nesbot/carbon/src/Carbon'),
|
||||
'Cake\\Utility\\' => array($vendorDir . '/cakephp/utility'),
|
||||
'Cake\\Datasource\\' => array($vendorDir . '/cakephp/datasource'),
|
||||
'Cake\\Database\\' => array($vendorDir . '/cakephp/database'),
|
||||
'Cake\\Core\\' => array($vendorDir . '/cakephp/core'),
|
||||
'Cake\\Chronos\\' => array($vendorDir . '/cakephp/chronos/src'),
|
||||
'Brick\\Math\\' => array($vendorDir . '/brick/math/src'),
|
||||
'Barryvdh\\LaravelIdeHelper\\' => array($vendorDir . '/barryvdh/laravel-ide-helper/src'),
|
||||
'Barryvdh\\Debugbar\\' => array($vendorDir . '/barryvdh/laravel-debugbar/src'),
|
||||
|
||||
Vendored
+461
-7
@@ -12,14 +12,15 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'e69f7f6ee287b969198c3c9d6777bd38' => __DIR__ . '/..' . '/symfony/polyfill-intl-normalizer/bootstrap.php',
|
||||
'320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php',
|
||||
'8825ede83f2f289127722d4e842cf7e8' => __DIR__ . '/..' . '/symfony/polyfill-intl-grapheme/bootstrap.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'b6b991a57620e2fb6b2f66f03fe9ddc2' => __DIR__ . '/..' . '/symfony/string/Resources/functions.php',
|
||||
'662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php',
|
||||
'667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php',
|
||||
'25072dd6e2470089de65ae7bf11d3109' => __DIR__ . '/..' . '/symfony/polyfill-php72/bootstrap.php',
|
||||
'f598d06aa772fa33d905e87be6398fb1' => __DIR__ . '/..' . '/symfony/polyfill-intl-idn/bootstrap.php',
|
||||
'7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php',
|
||||
'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php',
|
||||
'37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php',
|
||||
'72142d7b40a3a0b14e91825290b5ad82' => __DIR__ . '/..' . '/cakephp/core/functions.php',
|
||||
'35a6ad97d21e794e7e22a17d806652e4' => __DIR__ . '/..' . '/nunomaduro/termwind/src/Functions.php',
|
||||
'2203a247e6fda86070a5e4e07aed533a' => __DIR__ . '/..' . '/symfony/clock/Resources/now.php',
|
||||
'09f6b20656683369174dd6fa83b7e5fb' => __DIR__ . '/..' . '/symfony/polyfill-uuid/bootstrap.php',
|
||||
@@ -34,6 +35,7 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'58571171fd5812e6e447dce228f52f4d' => __DIR__ . '/..' . '/laravel/framework/src/Illuminate/Support/helpers.php',
|
||||
'6124b4c8570aa390c21fafd04a26c69f' => __DIR__ . '/..' . '/myclabs/deep-copy/src/DeepCopy/deep_copy.php',
|
||||
'3bd81c9b8fcc150b69d8b63b4d2ccf23' => __DIR__ . '/..' . '/spatie/flare-client-php/src/helpers.php',
|
||||
'948ad5488880985ff1c06721a4e447fe' => __DIR__ . '/..' . '/cakephp/utility/bootstrap.php',
|
||||
'ec07570ca5a812141189b1fa81503674' => __DIR__ . '/..' . '/phpunit/phpunit/src/Framework/Assert/Functions.php',
|
||||
'801c31d8ed748cfa537fa45402288c95' => __DIR__ . '/..' . '/psy/psysh/src/functions.php',
|
||||
'b84a5ba6d9a8ee8cf648b71c42c799b7' => __DIR__ . '/..' . '/akaunting/laravel-setting/src/helpers.php',
|
||||
@@ -107,10 +109,12 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Symfony\\Component\\HttpKernel\\' => 29,
|
||||
'Symfony\\Component\\HttpFoundation\\' => 33,
|
||||
'Symfony\\Component\\Finder\\' => 25,
|
||||
'Symfony\\Component\\Filesystem\\' => 29,
|
||||
'Symfony\\Component\\EventDispatcher\\' => 34,
|
||||
'Symfony\\Component\\ErrorHandler\\' => 31,
|
||||
'Symfony\\Component\\CssSelector\\' => 30,
|
||||
'Symfony\\Component\\Console\\' => 26,
|
||||
'Symfony\\Component\\Config\\' => 25,
|
||||
'Symfony\\Component\\Clock\\' => 24,
|
||||
'Spatie\\QueryBuilder\\Database\\Factories\\' => 39,
|
||||
'Spatie\\QueryBuilder\\' => 20,
|
||||
@@ -143,6 +147,7 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'PhpParser\\' => 10,
|
||||
'PhpOption\\' => 10,
|
||||
'PhpOffice\\PhpSpreadsheet\\' => 25,
|
||||
'Phinx\\' => 6,
|
||||
'ParagonIE\\ConstantTime\\' => 23,
|
||||
'ParaTest\\' => 9,
|
||||
'PHPStan\\PhpDocParser\\' => 21,
|
||||
@@ -164,6 +169,7 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'League\\MimeTypeDetection\\' => 25,
|
||||
'League\\Flysystem\\Local\\' => 23,
|
||||
'League\\Flysystem\\' => 17,
|
||||
'League\\Container\\' => 17,
|
||||
'League\\Config\\' => 14,
|
||||
'League\\CommonMark\\' => 18,
|
||||
'Laravel\\Ui\\' => 11,
|
||||
@@ -230,6 +236,11 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Complex\\' => 8,
|
||||
'Carbon\\Doctrine\\' => 16,
|
||||
'Carbon\\' => 7,
|
||||
'Cake\\Utility\\' => 13,
|
||||
'Cake\\Datasource\\' => 16,
|
||||
'Cake\\Database\\' => 14,
|
||||
'Cake\\Core\\' => 10,
|
||||
'Cake\\Chronos\\' => 13,
|
||||
),
|
||||
'B' =>
|
||||
array (
|
||||
@@ -257,8 +268,8 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
),
|
||||
'phpDocumentor\\Reflection\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
|
||||
1 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
|
||||
0 => __DIR__ . '/..' . '/phpdocumentor/type-resolver/src',
|
||||
1 => __DIR__ . '/..' . '/phpdocumentor/reflection-common/src',
|
||||
),
|
||||
'ZipStream\\' =>
|
||||
array (
|
||||
@@ -412,6 +423,10 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/finder',
|
||||
),
|
||||
'Symfony\\Component\\Filesystem\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/filesystem',
|
||||
),
|
||||
'Symfony\\Component\\EventDispatcher\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/event-dispatcher',
|
||||
@@ -428,6 +443,10 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/console',
|
||||
),
|
||||
'Symfony\\Component\\Config\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/config',
|
||||
),
|
||||
'Symfony\\Component\\Clock\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/symfony/clock',
|
||||
@@ -446,13 +465,13 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
),
|
||||
'Spatie\\LaravelIgnition\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/spatie/error-solutions/legacy/laravel-ignition',
|
||||
1 => __DIR__ . '/..' . '/spatie/laravel-ignition/src',
|
||||
0 => __DIR__ . '/..' . '/spatie/laravel-ignition/src',
|
||||
1 => __DIR__ . '/..' . '/spatie/error-solutions/legacy/laravel-ignition',
|
||||
),
|
||||
'Spatie\\Ignition\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/spatie/error-solutions/legacy/ignition',
|
||||
1 => __DIR__ . '/..' . '/spatie/ignition/src',
|
||||
0 => __DIR__ . '/..' . '/spatie/ignition/src',
|
||||
1 => __DIR__ . '/..' . '/spatie/error-solutions/legacy/ignition',
|
||||
),
|
||||
'Spatie\\FlareClient\\' =>
|
||||
array (
|
||||
@@ -535,6 +554,10 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet',
|
||||
),
|
||||
'Phinx\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx',
|
||||
),
|
||||
'ParagonIE\\ConstantTime\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/paragonie/constant_time_encoding/src',
|
||||
@@ -583,6 +606,10 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/flysystem/src',
|
||||
),
|
||||
'League\\Container\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/container/src',
|
||||
),
|
||||
'League\\Config\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/league/config/src',
|
||||
@@ -767,6 +794,26 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon',
|
||||
),
|
||||
'Cake\\Utility\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/cakephp/utility',
|
||||
),
|
||||
'Cake\\Datasource\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/cakephp/datasource',
|
||||
),
|
||||
'Cake\\Database\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/cakephp/database',
|
||||
),
|
||||
'Cake\\Core\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/cakephp/core',
|
||||
),
|
||||
'Cake\\Chronos\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/cakephp/chronos/src',
|
||||
),
|
||||
'Brick\\Math\\' =>
|
||||
array (
|
||||
0 => __DIR__ . '/..' . '/brick/math/src',
|
||||
@@ -1000,6 +1047,196 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Brick\\Math\\Internal\\Calculator\\GmpCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/GmpCalculator.php',
|
||||
'Brick\\Math\\Internal\\Calculator\\NativeCalculator' => __DIR__ . '/..' . '/brick/math/src/Internal/Calculator/NativeCalculator.php',
|
||||
'Brick\\Math\\RoundingMode' => __DIR__ . '/..' . '/brick/math/src/RoundingMode.php',
|
||||
'Cake\\Chronos\\Chronos' => __DIR__ . '/..' . '/cakephp/chronos/src/Chronos.php',
|
||||
'Cake\\Chronos\\ChronosDate' => __DIR__ . '/..' . '/cakephp/chronos/src/ChronosDate.php',
|
||||
'Cake\\Chronos\\ChronosDatePeriod' => __DIR__ . '/..' . '/cakephp/chronos/src/ChronosDatePeriod.php',
|
||||
'Cake\\Chronos\\ChronosInterval' => __DIR__ . '/..' . '/cakephp/chronos/src/ChronosInterval.php',
|
||||
'Cake\\Chronos\\ChronosPeriod' => __DIR__ . '/..' . '/cakephp/chronos/src/ChronosPeriod.php',
|
||||
'Cake\\Chronos\\ChronosTime' => __DIR__ . '/..' . '/cakephp/chronos/src/ChronosTime.php',
|
||||
'Cake\\Chronos\\ClockFactory' => __DIR__ . '/..' . '/cakephp/chronos/src/ClockFactory.php',
|
||||
'Cake\\Chronos\\DifferenceFormatter' => __DIR__ . '/..' . '/cakephp/chronos/src/DifferenceFormatter.php',
|
||||
'Cake\\Chronos\\DifferenceFormatterInterface' => __DIR__ . '/..' . '/cakephp/chronos/src/DifferenceFormatterInterface.php',
|
||||
'Cake\\Chronos\\FormattingTrait' => __DIR__ . '/..' . '/cakephp/chronos/src/FormattingTrait.php',
|
||||
'Cake\\Chronos\\Translator' => __DIR__ . '/..' . '/cakephp/chronos/src/Translator.php',
|
||||
'Cake\\Core\\App' => __DIR__ . '/..' . '/cakephp/core/App.php',
|
||||
'Cake\\Core\\Attribute\\Configure' => __DIR__ . '/..' . '/cakephp/core/Attribute/Configure.php',
|
||||
'Cake\\Core\\BasePlugin' => __DIR__ . '/..' . '/cakephp/core/BasePlugin.php',
|
||||
'Cake\\Core\\Configure' => __DIR__ . '/..' . '/cakephp/core/Configure.php',
|
||||
'Cake\\Core\\Configure\\ConfigEngineInterface' => __DIR__ . '/..' . '/cakephp/core/Configure/ConfigEngineInterface.php',
|
||||
'Cake\\Core\\Configure\\Engine\\IniConfig' => __DIR__ . '/..' . '/cakephp/core/Configure/Engine/IniConfig.php',
|
||||
'Cake\\Core\\Configure\\Engine\\JsonConfig' => __DIR__ . '/..' . '/cakephp/core/Configure/Engine/JsonConfig.php',
|
||||
'Cake\\Core\\Configure\\Engine\\PhpConfig' => __DIR__ . '/..' . '/cakephp/core/Configure/Engine/PhpConfig.php',
|
||||
'Cake\\Core\\Configure\\FileConfigTrait' => __DIR__ . '/..' . '/cakephp/core/Configure/FileConfigTrait.php',
|
||||
'Cake\\Core\\ConsoleApplicationInterface' => __DIR__ . '/..' . '/cakephp/core/ConsoleApplicationInterface.php',
|
||||
'Cake\\Core\\Container' => __DIR__ . '/..' . '/cakephp/core/Container.php',
|
||||
'Cake\\Core\\ContainerApplicationInterface' => __DIR__ . '/..' . '/cakephp/core/ContainerApplicationInterface.php',
|
||||
'Cake\\Core\\ContainerInterface' => __DIR__ . '/..' . '/cakephp/core/ContainerInterface.php',
|
||||
'Cake\\Core\\ConventionsTrait' => __DIR__ . '/..' . '/cakephp/core/ConventionsTrait.php',
|
||||
'Cake\\Core\\EventAwareApplicationInterface' => __DIR__ . '/..' . '/cakephp/core/EventAwareApplicationInterface.php',
|
||||
'Cake\\Core\\Exception\\CakeException' => __DIR__ . '/..' . '/cakephp/core/Exception/CakeException.php',
|
||||
'Cake\\Core\\Exception\\HttpErrorCodeInterface' => __DIR__ . '/..' . '/cakephp/core/Exception/HttpErrorCodeInterface.php',
|
||||
'Cake\\Core\\Exception\\MissingPluginException' => __DIR__ . '/..' . '/cakephp/core/Exception/MissingPluginException.php',
|
||||
'Cake\\Core\\HttpApplicationInterface' => __DIR__ . '/..' . '/cakephp/core/HttpApplicationInterface.php',
|
||||
'Cake\\Core\\InstanceConfigTrait' => __DIR__ . '/..' . '/cakephp/core/InstanceConfigTrait.php',
|
||||
'Cake\\Core\\ObjectRegistry' => __DIR__ . '/..' . '/cakephp/core/ObjectRegistry.php',
|
||||
'Cake\\Core\\Plugin' => __DIR__ . '/..' . '/cakephp/core/Plugin.php',
|
||||
'Cake\\Core\\PluginApplicationInterface' => __DIR__ . '/..' . '/cakephp/core/PluginApplicationInterface.php',
|
||||
'Cake\\Core\\PluginCollection' => __DIR__ . '/..' . '/cakephp/core/PluginCollection.php',
|
||||
'Cake\\Core\\PluginConfig' => __DIR__ . '/..' . '/cakephp/core/PluginConfig.php',
|
||||
'Cake\\Core\\PluginInterface' => __DIR__ . '/..' . '/cakephp/core/PluginInterface.php',
|
||||
'Cake\\Core\\Retry\\CommandRetry' => __DIR__ . '/..' . '/cakephp/core/Retry/CommandRetry.php',
|
||||
'Cake\\Core\\Retry\\RetryStrategyInterface' => __DIR__ . '/..' . '/cakephp/core/Retry/RetryStrategyInterface.php',
|
||||
'Cake\\Core\\ServiceConfig' => __DIR__ . '/..' . '/cakephp/core/ServiceConfig.php',
|
||||
'Cake\\Core\\ServiceProvider' => __DIR__ . '/..' . '/cakephp/core/ServiceProvider.php',
|
||||
'Cake\\Core\\StaticConfigTrait' => __DIR__ . '/..' . '/cakephp/core/StaticConfigTrait.php',
|
||||
'Cake\\Core\\TestSuite\\ContainerStubTrait' => __DIR__ . '/..' . '/cakephp/core/TestSuite/ContainerStubTrait.php',
|
||||
'Cake\\Database\\Connection' => __DIR__ . '/..' . '/cakephp/database/Connection.php',
|
||||
'Cake\\Database\\ConstraintsInterface' => __DIR__ . '/..' . '/cakephp/database/ConstraintsInterface.php',
|
||||
'Cake\\Database\\Driver' => __DIR__ . '/..' . '/cakephp/database/Driver.php',
|
||||
'Cake\\Database\\DriverFeatureEnum' => __DIR__ . '/..' . '/cakephp/database/DriverFeatureEnum.php',
|
||||
'Cake\\Database\\Driver\\Mysql' => __DIR__ . '/..' . '/cakephp/database/Driver/Mysql.php',
|
||||
'Cake\\Database\\Driver\\Postgres' => __DIR__ . '/..' . '/cakephp/database/Driver/Postgres.php',
|
||||
'Cake\\Database\\Driver\\Sqlite' => __DIR__ . '/..' . '/cakephp/database/Driver/Sqlite.php',
|
||||
'Cake\\Database\\Driver\\Sqlserver' => __DIR__ . '/..' . '/cakephp/database/Driver/Sqlserver.php',
|
||||
'Cake\\Database\\Driver\\TupleComparisonTranslatorTrait' => __DIR__ . '/..' . '/cakephp/database/Driver/TupleComparisonTranslatorTrait.php',
|
||||
'Cake\\Database\\Exception\\DatabaseException' => __DIR__ . '/..' . '/cakephp/database/Exception/DatabaseException.php',
|
||||
'Cake\\Database\\Exception\\MissingConnectionException' => __DIR__ . '/..' . '/cakephp/database/Exception/MissingConnectionException.php',
|
||||
'Cake\\Database\\Exception\\MissingDriverException' => __DIR__ . '/..' . '/cakephp/database/Exception/MissingDriverException.php',
|
||||
'Cake\\Database\\Exception\\MissingExtensionException' => __DIR__ . '/..' . '/cakephp/database/Exception/MissingExtensionException.php',
|
||||
'Cake\\Database\\Exception\\NestedTransactionRollbackException' => __DIR__ . '/..' . '/cakephp/database/Exception/NestedTransactionRollbackException.php',
|
||||
'Cake\\Database\\Exception\\QueryException' => __DIR__ . '/..' . '/cakephp/database/Exception/QueryException.php',
|
||||
'Cake\\Database\\ExpressionInterface' => __DIR__ . '/..' . '/cakephp/database/ExpressionInterface.php',
|
||||
'Cake\\Database\\Expression\\AggregateExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/AggregateExpression.php',
|
||||
'Cake\\Database\\Expression\\BetweenExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/BetweenExpression.php',
|
||||
'Cake\\Database\\Expression\\CaseExpressionTrait' => __DIR__ . '/..' . '/cakephp/database/Expression/CaseExpressionTrait.php',
|
||||
'Cake\\Database\\Expression\\CaseStatementExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/CaseStatementExpression.php',
|
||||
'Cake\\Database\\Expression\\CommonTableExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/CommonTableExpression.php',
|
||||
'Cake\\Database\\Expression\\ComparisonExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/ComparisonExpression.php',
|
||||
'Cake\\Database\\Expression\\FieldInterface' => __DIR__ . '/..' . '/cakephp/database/Expression/FieldInterface.php',
|
||||
'Cake\\Database\\Expression\\FieldTrait' => __DIR__ . '/..' . '/cakephp/database/Expression/FieldTrait.php',
|
||||
'Cake\\Database\\Expression\\FunctionExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/FunctionExpression.php',
|
||||
'Cake\\Database\\Expression\\IdentifierExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/IdentifierExpression.php',
|
||||
'Cake\\Database\\Expression\\OrderByExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/OrderByExpression.php',
|
||||
'Cake\\Database\\Expression\\OrderClauseExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/OrderClauseExpression.php',
|
||||
'Cake\\Database\\Expression\\QueryExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/QueryExpression.php',
|
||||
'Cake\\Database\\Expression\\StringExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/StringExpression.php',
|
||||
'Cake\\Database\\Expression\\TupleComparison' => __DIR__ . '/..' . '/cakephp/database/Expression/TupleComparison.php',
|
||||
'Cake\\Database\\Expression\\UnaryExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/UnaryExpression.php',
|
||||
'Cake\\Database\\Expression\\ValuesExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/ValuesExpression.php',
|
||||
'Cake\\Database\\Expression\\WhenThenExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/WhenThenExpression.php',
|
||||
'Cake\\Database\\Expression\\WindowExpression' => __DIR__ . '/..' . '/cakephp/database/Expression/WindowExpression.php',
|
||||
'Cake\\Database\\Expression\\WindowInterface' => __DIR__ . '/..' . '/cakephp/database/Expression/WindowInterface.php',
|
||||
'Cake\\Database\\FieldTypeConverter' => __DIR__ . '/..' . '/cakephp/database/FieldTypeConverter.php',
|
||||
'Cake\\Database\\FunctionsBuilder' => __DIR__ . '/..' . '/cakephp/database/FunctionsBuilder.php',
|
||||
'Cake\\Database\\IdentifierQuoter' => __DIR__ . '/..' . '/cakephp/database/IdentifierQuoter.php',
|
||||
'Cake\\Database\\Log\\LoggedQuery' => __DIR__ . '/..' . '/cakephp/database/Log/LoggedQuery.php',
|
||||
'Cake\\Database\\Log\\QueryLogger' => __DIR__ . '/..' . '/cakephp/database/Log/QueryLogger.php',
|
||||
'Cake\\Database\\PostgresCompiler' => __DIR__ . '/..' . '/cakephp/database/PostgresCompiler.php',
|
||||
'Cake\\Database\\Query' => __DIR__ . '/..' . '/cakephp/database/Query.php',
|
||||
'Cake\\Database\\QueryCompiler' => __DIR__ . '/..' . '/cakephp/database/QueryCompiler.php',
|
||||
'Cake\\Database\\Query\\DeleteQuery' => __DIR__ . '/..' . '/cakephp/database/Query/DeleteQuery.php',
|
||||
'Cake\\Database\\Query\\InsertQuery' => __DIR__ . '/..' . '/cakephp/database/Query/InsertQuery.php',
|
||||
'Cake\\Database\\Query\\QueryFactory' => __DIR__ . '/..' . '/cakephp/database/Query/QueryFactory.php',
|
||||
'Cake\\Database\\Query\\SelectQuery' => __DIR__ . '/..' . '/cakephp/database/Query/SelectQuery.php',
|
||||
'Cake\\Database\\Query\\UpdateQuery' => __DIR__ . '/..' . '/cakephp/database/Query/UpdateQuery.php',
|
||||
'Cake\\Database\\Retry\\ErrorCodeWaitStrategy' => __DIR__ . '/..' . '/cakephp/database/Retry/ErrorCodeWaitStrategy.php',
|
||||
'Cake\\Database\\Retry\\ReconnectStrategy' => __DIR__ . '/..' . '/cakephp/database/Retry/ReconnectStrategy.php',
|
||||
'Cake\\Database\\SchemaCache' => __DIR__ . '/..' . '/cakephp/database/SchemaCache.php',
|
||||
'Cake\\Database\\Schema\\CachedCollection' => __DIR__ . '/..' . '/cakephp/database/Schema/CachedCollection.php',
|
||||
'Cake\\Database\\Schema\\CheckConstraint' => __DIR__ . '/..' . '/cakephp/database/Schema/CheckConstraint.php',
|
||||
'Cake\\Database\\Schema\\Collection' => __DIR__ . '/..' . '/cakephp/database/Schema/Collection.php',
|
||||
'Cake\\Database\\Schema\\CollectionInterface' => __DIR__ . '/..' . '/cakephp/database/Schema/CollectionInterface.php',
|
||||
'Cake\\Database\\Schema\\Column' => __DIR__ . '/..' . '/cakephp/database/Schema/Column.php',
|
||||
'Cake\\Database\\Schema\\Constraint' => __DIR__ . '/..' . '/cakephp/database/Schema/Constraint.php',
|
||||
'Cake\\Database\\Schema\\ForeignKey' => __DIR__ . '/..' . '/cakephp/database/Schema/ForeignKey.php',
|
||||
'Cake\\Database\\Schema\\Index' => __DIR__ . '/..' . '/cakephp/database/Schema/Index.php',
|
||||
'Cake\\Database\\Schema\\MysqlSchemaDialect' => __DIR__ . '/..' . '/cakephp/database/Schema/MysqlSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\PostgresSchemaDialect' => __DIR__ . '/..' . '/cakephp/database/Schema/PostgresSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\SchemaDialect' => __DIR__ . '/..' . '/cakephp/database/Schema/SchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\SqlGeneratorInterface' => __DIR__ . '/..' . '/cakephp/database/Schema/SqlGeneratorInterface.php',
|
||||
'Cake\\Database\\Schema\\SqliteSchemaDialect' => __DIR__ . '/..' . '/cakephp/database/Schema/SqliteSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\SqlserverSchemaDialect' => __DIR__ . '/..' . '/cakephp/database/Schema/SqlserverSchemaDialect.php',
|
||||
'Cake\\Database\\Schema\\TableSchema' => __DIR__ . '/..' . '/cakephp/database/Schema/TableSchema.php',
|
||||
'Cake\\Database\\Schema\\TableSchemaInterface' => __DIR__ . '/..' . '/cakephp/database/Schema/TableSchemaInterface.php',
|
||||
'Cake\\Database\\Schema\\UniqueKey' => __DIR__ . '/..' . '/cakephp/database/Schema/UniqueKey.php',
|
||||
'Cake\\Database\\SqlserverCompiler' => __DIR__ . '/..' . '/cakephp/database/SqlserverCompiler.php',
|
||||
'Cake\\Database\\StatementInterface' => __DIR__ . '/..' . '/cakephp/database/StatementInterface.php',
|
||||
'Cake\\Database\\Statement\\SqliteStatement' => __DIR__ . '/..' . '/cakephp/database/Statement/SqliteStatement.php',
|
||||
'Cake\\Database\\Statement\\SqlserverStatement' => __DIR__ . '/..' . '/cakephp/database/Statement/SqlserverStatement.php',
|
||||
'Cake\\Database\\Statement\\Statement' => __DIR__ . '/..' . '/cakephp/database/Statement/Statement.php',
|
||||
'Cake\\Database\\TypeFactory' => __DIR__ . '/..' . '/cakephp/database/TypeFactory.php',
|
||||
'Cake\\Database\\TypeInterface' => __DIR__ . '/..' . '/cakephp/database/TypeInterface.php',
|
||||
'Cake\\Database\\TypeMap' => __DIR__ . '/..' . '/cakephp/database/TypeMap.php',
|
||||
'Cake\\Database\\TypeMapTrait' => __DIR__ . '/..' . '/cakephp/database/TypeMapTrait.php',
|
||||
'Cake\\Database\\Type\\BaseType' => __DIR__ . '/..' . '/cakephp/database/Type/BaseType.php',
|
||||
'Cake\\Database\\Type\\BatchCastingInterface' => __DIR__ . '/..' . '/cakephp/database/Type/BatchCastingInterface.php',
|
||||
'Cake\\Database\\Type\\BinaryType' => __DIR__ . '/..' . '/cakephp/database/Type/BinaryType.php',
|
||||
'Cake\\Database\\Type\\BinaryUuidType' => __DIR__ . '/..' . '/cakephp/database/Type/BinaryUuidType.php',
|
||||
'Cake\\Database\\Type\\BoolType' => __DIR__ . '/..' . '/cakephp/database/Type/BoolType.php',
|
||||
'Cake\\Database\\Type\\ColumnSchemaAwareInterface' => __DIR__ . '/..' . '/cakephp/database/Type/ColumnSchemaAwareInterface.php',
|
||||
'Cake\\Database\\Type\\DateTimeFractionalType' => __DIR__ . '/..' . '/cakephp/database/Type/DateTimeFractionalType.php',
|
||||
'Cake\\Database\\Type\\DateTimeTimezoneType' => __DIR__ . '/..' . '/cakephp/database/Type/DateTimeTimezoneType.php',
|
||||
'Cake\\Database\\Type\\DateTimeType' => __DIR__ . '/..' . '/cakephp/database/Type/DateTimeType.php',
|
||||
'Cake\\Database\\Type\\DateType' => __DIR__ . '/..' . '/cakephp/database/Type/DateType.php',
|
||||
'Cake\\Database\\Type\\DecimalType' => __DIR__ . '/..' . '/cakephp/database/Type/DecimalType.php',
|
||||
'Cake\\Database\\Type\\EnumLabelInterface' => __DIR__ . '/..' . '/cakephp/database/Type/EnumLabelInterface.php',
|
||||
'Cake\\Database\\Type\\EnumType' => __DIR__ . '/..' . '/cakephp/database/Type/EnumType.php',
|
||||
'Cake\\Database\\Type\\ExpressionTypeCasterTrait' => __DIR__ . '/..' . '/cakephp/database/Type/ExpressionTypeCasterTrait.php',
|
||||
'Cake\\Database\\Type\\ExpressionTypeInterface' => __DIR__ . '/..' . '/cakephp/database/Type/ExpressionTypeInterface.php',
|
||||
'Cake\\Database\\Type\\FloatType' => __DIR__ . '/..' . '/cakephp/database/Type/FloatType.php',
|
||||
'Cake\\Database\\Type\\IntegerType' => __DIR__ . '/..' . '/cakephp/database/Type/IntegerType.php',
|
||||
'Cake\\Database\\Type\\JsonType' => __DIR__ . '/..' . '/cakephp/database/Type/JsonType.php',
|
||||
'Cake\\Database\\Type\\OptionalConvertInterface' => __DIR__ . '/..' . '/cakephp/database/Type/OptionalConvertInterface.php',
|
||||
'Cake\\Database\\Type\\StringType' => __DIR__ . '/..' . '/cakephp/database/Type/StringType.php',
|
||||
'Cake\\Database\\Type\\TimeType' => __DIR__ . '/..' . '/cakephp/database/Type/TimeType.php',
|
||||
'Cake\\Database\\Type\\UuidType' => __DIR__ . '/..' . '/cakephp/database/Type/UuidType.php',
|
||||
'Cake\\Database\\TypedResultInterface' => __DIR__ . '/..' . '/cakephp/database/TypedResultInterface.php',
|
||||
'Cake\\Database\\TypedResultTrait' => __DIR__ . '/..' . '/cakephp/database/TypedResultTrait.php',
|
||||
'Cake\\Database\\ValueBinder' => __DIR__ . '/..' . '/cakephp/database/ValueBinder.php',
|
||||
'Cake\\Datasource\\ConnectionInterface' => __DIR__ . '/..' . '/cakephp/datasource/ConnectionInterface.php',
|
||||
'Cake\\Datasource\\ConnectionManager' => __DIR__ . '/..' . '/cakephp/datasource/ConnectionManager.php',
|
||||
'Cake\\Datasource\\ConnectionRegistry' => __DIR__ . '/..' . '/cakephp/datasource/ConnectionRegistry.php',
|
||||
'Cake\\Datasource\\EntityInterface' => __DIR__ . '/..' . '/cakephp/datasource/EntityInterface.php',
|
||||
'Cake\\Datasource\\EntityTrait' => __DIR__ . '/..' . '/cakephp/datasource/EntityTrait.php',
|
||||
'Cake\\Datasource\\Exception\\InvalidPrimaryKeyException' => __DIR__ . '/..' . '/cakephp/datasource/Exception/InvalidPrimaryKeyException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingDatasourceConfigException' => __DIR__ . '/..' . '/cakephp/datasource/Exception/MissingDatasourceConfigException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingDatasourceException' => __DIR__ . '/..' . '/cakephp/datasource/Exception/MissingDatasourceException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingModelException' => __DIR__ . '/..' . '/cakephp/datasource/Exception/MissingModelException.php',
|
||||
'Cake\\Datasource\\Exception\\MissingPropertyException' => __DIR__ . '/..' . '/cakephp/datasource/Exception/MissingPropertyException.php',
|
||||
'Cake\\Datasource\\Exception\\RecordNotFoundException' => __DIR__ . '/..' . '/cakephp/datasource/Exception/RecordNotFoundException.php',
|
||||
'Cake\\Datasource\\FactoryLocator' => __DIR__ . '/..' . '/cakephp/datasource/FactoryLocator.php',
|
||||
'Cake\\Datasource\\FixtureInterface' => __DIR__ . '/..' . '/cakephp/datasource/FixtureInterface.php',
|
||||
'Cake\\Datasource\\InvalidPropertyInterface' => __DIR__ . '/..' . '/cakephp/datasource/InvalidPropertyInterface.php',
|
||||
'Cake\\Datasource\\Locator\\AbstractLocator' => __DIR__ . '/..' . '/cakephp/datasource/Locator/AbstractLocator.php',
|
||||
'Cake\\Datasource\\Locator\\LocatorInterface' => __DIR__ . '/..' . '/cakephp/datasource/Locator/LocatorInterface.php',
|
||||
'Cake\\Datasource\\ModelAwareTrait' => __DIR__ . '/..' . '/cakephp/datasource/ModelAwareTrait.php',
|
||||
'Cake\\Datasource\\Paging\\Exception\\PageOutOfBoundsException' => __DIR__ . '/..' . '/cakephp/datasource/Paging/Exception/PageOutOfBoundsException.php',
|
||||
'Cake\\Datasource\\Paging\\NumericPaginator' => __DIR__ . '/..' . '/cakephp/datasource/Paging/NumericPaginator.php',
|
||||
'Cake\\Datasource\\Paging\\PaginatedInterface' => __DIR__ . '/..' . '/cakephp/datasource/Paging/PaginatedInterface.php',
|
||||
'Cake\\Datasource\\Paging\\PaginatedResultSet' => __DIR__ . '/..' . '/cakephp/datasource/Paging/PaginatedResultSet.php',
|
||||
'Cake\\Datasource\\Paging\\PaginatorInterface' => __DIR__ . '/..' . '/cakephp/datasource/Paging/PaginatorInterface.php',
|
||||
'Cake\\Datasource\\Paging\\SimplePaginator' => __DIR__ . '/..' . '/cakephp/datasource/Paging/SimplePaginator.php',
|
||||
'Cake\\Datasource\\Paging\\SortField' => __DIR__ . '/..' . '/cakephp/datasource/Paging/SortField.php',
|
||||
'Cake\\Datasource\\Paging\\SortableFieldsBuilder' => __DIR__ . '/..' . '/cakephp/datasource/Paging/SortableFieldsBuilder.php',
|
||||
'Cake\\Datasource\\QueryCacher' => __DIR__ . '/..' . '/cakephp/datasource/QueryCacher.php',
|
||||
'Cake\\Datasource\\QueryInterface' => __DIR__ . '/..' . '/cakephp/datasource/QueryInterface.php',
|
||||
'Cake\\Datasource\\RepositoryInterface' => __DIR__ . '/..' . '/cakephp/datasource/RepositoryInterface.php',
|
||||
'Cake\\Datasource\\ResultSetDecorator' => __DIR__ . '/..' . '/cakephp/datasource/ResultSetDecorator.php',
|
||||
'Cake\\Datasource\\ResultSetInterface' => __DIR__ . '/..' . '/cakephp/datasource/ResultSetInterface.php',
|
||||
'Cake\\Datasource\\RuleInvoker' => __DIR__ . '/..' . '/cakephp/datasource/RuleInvoker.php',
|
||||
'Cake\\Datasource\\RulesAwareTrait' => __DIR__ . '/..' . '/cakephp/datasource/RulesAwareTrait.php',
|
||||
'Cake\\Datasource\\RulesChecker' => __DIR__ . '/..' . '/cakephp/datasource/RulesChecker.php',
|
||||
'Cake\\Datasource\\SchemaInterface' => __DIR__ . '/..' . '/cakephp/datasource/SchemaInterface.php',
|
||||
'Cake\\Utility\\CookieCryptTrait' => __DIR__ . '/..' . '/cakephp/utility/CookieCryptTrait.php',
|
||||
'Cake\\Utility\\Crypto\\OpenSsl' => __DIR__ . '/..' . '/cakephp/utility/Crypto/OpenSsl.php',
|
||||
'Cake\\Utility\\Exception\\XmlException' => __DIR__ . '/..' . '/cakephp/utility/Exception/XmlException.php',
|
||||
'Cake\\Utility\\Filesystem' => __DIR__ . '/..' . '/cakephp/utility/Filesystem.php',
|
||||
'Cake\\Utility\\Hash' => __DIR__ . '/..' . '/cakephp/utility/Hash.php',
|
||||
'Cake\\Utility\\Inflector' => __DIR__ . '/..' . '/cakephp/utility/Inflector.php',
|
||||
'Cake\\Utility\\MergeVariablesTrait' => __DIR__ . '/..' . '/cakephp/utility/MergeVariablesTrait.php',
|
||||
'Cake\\Utility\\Security' => __DIR__ . '/..' . '/cakephp/utility/Security.php',
|
||||
'Cake\\Utility\\Text' => __DIR__ . '/..' . '/cakephp/utility/Text.php',
|
||||
'Cake\\Utility\\Xml' => __DIR__ . '/..' . '/cakephp/utility/Xml.php',
|
||||
'Carbon\\AbstractTranslator' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/AbstractTranslator.php',
|
||||
'Carbon\\Callback' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Callback.php',
|
||||
'Carbon\\Carbon' => __DIR__ . '/..' . '/nesbot/carbon/src/Carbon/Carbon.php',
|
||||
@@ -4229,6 +4466,55 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'League\\Config\\MutableConfigurationInterface' => __DIR__ . '/..' . '/league/config/src/MutableConfigurationInterface.php',
|
||||
'League\\Config\\ReadOnlyConfiguration' => __DIR__ . '/..' . '/league/config/src/ReadOnlyConfiguration.php',
|
||||
'League\\Config\\SchemaBuilderInterface' => __DIR__ . '/..' . '/league/config/src/SchemaBuilderInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentInterface' => __DIR__ . '/..' . '/league/container/src/Argument/ArgumentInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentReflectorInterface' => __DIR__ . '/..' . '/league/container/src/Argument/ArgumentReflectorInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentReflectorTrait' => __DIR__ . '/..' . '/league/container/src/Argument/ArgumentReflectorTrait.php',
|
||||
'League\\Container\\Argument\\ArgumentResolverInterface' => __DIR__ . '/..' . '/league/container/src/Argument/ArgumentResolverInterface.php',
|
||||
'League\\Container\\Argument\\ArgumentResolverTrait' => __DIR__ . '/..' . '/league/container/src/Argument/ArgumentResolverTrait.php',
|
||||
'League\\Container\\Argument\\DefaultValueArgument' => __DIR__ . '/..' . '/league/container/src/Argument/DefaultValueArgument.php',
|
||||
'League\\Container\\Argument\\DefaultValueInterface' => __DIR__ . '/..' . '/league/container/src/Argument/DefaultValueInterface.php',
|
||||
'League\\Container\\Argument\\LiteralArgument' => __DIR__ . '/..' . '/league/container/src/Argument/LiteralArgument.php',
|
||||
'League\\Container\\Argument\\LiteralArgumentInterface' => __DIR__ . '/..' . '/league/container/src/Argument/LiteralArgumentInterface.php',
|
||||
'League\\Container\\Argument\\Literal\\ArrayArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/ArrayArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\BooleanArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/BooleanArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\CallableArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/CallableArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\FloatArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/FloatArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\IntegerArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/IntegerArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\ObjectArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/ObjectArgument.php',
|
||||
'League\\Container\\Argument\\Literal\\StringArgument' => __DIR__ . '/..' . '/league/container/src/Argument/Literal/StringArgument.php',
|
||||
'League\\Container\\Argument\\ResolvableArgument' => __DIR__ . '/..' . '/league/container/src/Argument/ResolvableArgument.php',
|
||||
'League\\Container\\Argument\\ResolvableArgumentInterface' => __DIR__ . '/..' . '/league/container/src/Argument/ResolvableArgumentInterface.php',
|
||||
'League\\Container\\Attribute\\AttributeInterface' => __DIR__ . '/..' . '/league/container/src/Attribute/AttributeInterface.php',
|
||||
'League\\Container\\Attribute\\Inject' => __DIR__ . '/..' . '/league/container/src/Attribute/Inject.php',
|
||||
'League\\Container\\Attribute\\Resolve' => __DIR__ . '/..' . '/league/container/src/Attribute/Resolve.php',
|
||||
'League\\Container\\Container' => __DIR__ . '/..' . '/league/container/src/Container.php',
|
||||
'League\\Container\\ContainerAwareInterface' => __DIR__ . '/..' . '/league/container/src/ContainerAwareInterface.php',
|
||||
'League\\Container\\ContainerAwareTrait' => __DIR__ . '/..' . '/league/container/src/ContainerAwareTrait.php',
|
||||
'League\\Container\\DefinitionContainerInterface' => __DIR__ . '/..' . '/league/container/src/DefinitionContainerInterface.php',
|
||||
'League\\Container\\Definition\\Definition' => __DIR__ . '/..' . '/league/container/src/Definition/Definition.php',
|
||||
'League\\Container\\Definition\\DefinitionAggregate' => __DIR__ . '/..' . '/league/container/src/Definition/DefinitionAggregate.php',
|
||||
'League\\Container\\Definition\\DefinitionAggregateInterface' => __DIR__ . '/..' . '/league/container/src/Definition/DefinitionAggregateInterface.php',
|
||||
'League\\Container\\Definition\\DefinitionInterface' => __DIR__ . '/..' . '/league/container/src/Definition/DefinitionInterface.php',
|
||||
'League\\Container\\Event\\BeforeResolveEvent' => __DIR__ . '/..' . '/league/container/src/Event/BeforeResolveEvent.php',
|
||||
'League\\Container\\Event\\ContainerEvent' => __DIR__ . '/..' . '/league/container/src/Event/ContainerEvent.php',
|
||||
'League\\Container\\Event\\DefinitionResolvedEvent' => __DIR__ . '/..' . '/league/container/src/Event/DefinitionResolvedEvent.php',
|
||||
'League\\Container\\Event\\EventAwareTrait' => __DIR__ . '/..' . '/league/container/src/Event/EventAwareTrait.php',
|
||||
'League\\Container\\Event\\EventDispatcher' => __DIR__ . '/..' . '/league/container/src/Event/EventDispatcher.php',
|
||||
'League\\Container\\Event\\EventFilter' => __DIR__ . '/..' . '/league/container/src/Event/EventFilter.php',
|
||||
'League\\Container\\Event\\OnDefineEvent' => __DIR__ . '/..' . '/league/container/src/Event/OnDefineEvent.php',
|
||||
'League\\Container\\Event\\ServiceResolvedEvent' => __DIR__ . '/..' . '/league/container/src/Event/ServiceResolvedEvent.php',
|
||||
'League\\Container\\Exception\\ContainerException' => __DIR__ . '/..' . '/league/container/src/Exception/ContainerException.php',
|
||||
'League\\Container\\Exception\\NotFoundException' => __DIR__ . '/..' . '/league/container/src/Exception/NotFoundException.php',
|
||||
'League\\Container\\Inflector\\Inflector' => __DIR__ . '/..' . '/league/container/src/Inflector/Inflector.php',
|
||||
'League\\Container\\Inflector\\InflectorAggregate' => __DIR__ . '/..' . '/league/container/src/Inflector/InflectorAggregate.php',
|
||||
'League\\Container\\Inflector\\InflectorAggregateInterface' => __DIR__ . '/..' . '/league/container/src/Inflector/InflectorAggregateInterface.php',
|
||||
'League\\Container\\Inflector\\InflectorInterface' => __DIR__ . '/..' . '/league/container/src/Inflector/InflectorInterface.php',
|
||||
'League\\Container\\ReflectionContainer' => __DIR__ . '/..' . '/league/container/src/ReflectionContainer.php',
|
||||
'League\\Container\\ServiceProvider\\AbstractServiceProvider' => __DIR__ . '/..' . '/league/container/src/ServiceProvider/AbstractServiceProvider.php',
|
||||
'League\\Container\\ServiceProvider\\BootableServiceProviderInterface' => __DIR__ . '/..' . '/league/container/src/ServiceProvider/BootableServiceProviderInterface.php',
|
||||
'League\\Container\\ServiceProvider\\ServiceProviderAggregate' => __DIR__ . '/..' . '/league/container/src/ServiceProvider/ServiceProviderAggregate.php',
|
||||
'League\\Container\\ServiceProvider\\ServiceProviderAggregateInterface' => __DIR__ . '/..' . '/league/container/src/ServiceProvider/ServiceProviderAggregateInterface.php',
|
||||
'League\\Container\\ServiceProvider\\ServiceProviderInterface' => __DIR__ . '/..' . '/league/container/src/ServiceProvider/ServiceProviderInterface.php',
|
||||
'League\\Flysystem\\CalculateChecksumFromStream' => __DIR__ . '/..' . '/league/flysystem/src/CalculateChecksumFromStream.php',
|
||||
'League\\Flysystem\\ChecksumAlgoIsNotSupported' => __DIR__ . '/..' . '/league/flysystem/src/ChecksumAlgoIsNotSupported.php',
|
||||
'League\\Flysystem\\ChecksumProvider' => __DIR__ . '/..' . '/league/flysystem/src/ChecksumProvider.php',
|
||||
@@ -5737,6 +6023,76 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'PharIo\\Version\\VersionConstraintParser' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintParser.php',
|
||||
'PharIo\\Version\\VersionConstraintValue' => __DIR__ . '/..' . '/phar-io/version/src/VersionConstraintValue.php',
|
||||
'PharIo\\Version\\VersionNumber' => __DIR__ . '/..' . '/phar-io/version/src/VersionNumber.php',
|
||||
'Phinx\\Config\\Config' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/Config.php',
|
||||
'Phinx\\Config\\ConfigInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/ConfigInterface.php',
|
||||
'Phinx\\Config\\FeatureFlags' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/FeatureFlags.php',
|
||||
'Phinx\\Config\\NamespaceAwareInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareInterface.php',
|
||||
'Phinx\\Config\\NamespaceAwareTrait' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Config/NamespaceAwareTrait.php',
|
||||
'Phinx\\Console\\Command\\AbstractCommand' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/AbstractCommand.php',
|
||||
'Phinx\\Console\\Command\\Breakpoint' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Breakpoint.php',
|
||||
'Phinx\\Console\\Command\\Create' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Create.php',
|
||||
'Phinx\\Console\\Command\\Init' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Init.php',
|
||||
'Phinx\\Console\\Command\\ListAliases' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/ListAliases.php',
|
||||
'Phinx\\Console\\Command\\Migrate' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Migrate.php',
|
||||
'Phinx\\Console\\Command\\Rollback' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Rollback.php',
|
||||
'Phinx\\Console\\Command\\SeedCreate' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/SeedCreate.php',
|
||||
'Phinx\\Console\\Command\\SeedRun' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/SeedRun.php',
|
||||
'Phinx\\Console\\Command\\Status' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Status.php',
|
||||
'Phinx\\Console\\Command\\Test' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/Command/Test.php',
|
||||
'Phinx\\Console\\PhinxApplication' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Console/PhinxApplication.php',
|
||||
'Phinx\\Db\\Action\\Action' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/Action.php',
|
||||
'Phinx\\Db\\Action\\AddColumn' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/AddColumn.php',
|
||||
'Phinx\\Db\\Action\\AddForeignKey' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/AddForeignKey.php',
|
||||
'Phinx\\Db\\Action\\AddIndex' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/AddIndex.php',
|
||||
'Phinx\\Db\\Action\\ChangeColumn' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/ChangeColumn.php',
|
||||
'Phinx\\Db\\Action\\ChangeComment' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/ChangeComment.php',
|
||||
'Phinx\\Db\\Action\\ChangePrimaryKey' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/ChangePrimaryKey.php',
|
||||
'Phinx\\Db\\Action\\CreateTable' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/CreateTable.php',
|
||||
'Phinx\\Db\\Action\\DropForeignKey' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/DropForeignKey.php',
|
||||
'Phinx\\Db\\Action\\DropIndex' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/DropIndex.php',
|
||||
'Phinx\\Db\\Action\\DropTable' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/DropTable.php',
|
||||
'Phinx\\Db\\Action\\RemoveColumn' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/RemoveColumn.php',
|
||||
'Phinx\\Db\\Action\\RenameColumn' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/RenameColumn.php',
|
||||
'Phinx\\Db\\Action\\RenameTable' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Action/RenameTable.php',
|
||||
'Phinx\\Db\\Adapter\\AbstractAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AbstractAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\AdapterFactory' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterFactory.php',
|
||||
'Phinx\\Db\\Adapter\\AdapterInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterInterface.php',
|
||||
'Phinx\\Db\\Adapter\\AdapterWrapper' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/AdapterWrapper.php',
|
||||
'Phinx\\Db\\Adapter\\DirectActionInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/DirectActionInterface.php',
|
||||
'Phinx\\Db\\Adapter\\MysqlAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/MysqlAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\PdoAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/PdoAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\PostgresAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/PostgresAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\ProxyAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/ProxyAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\SQLiteAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/SQLiteAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\SqlServerAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/SqlServerAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\TablePrefixAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/TablePrefixAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\TimedOutputAdapter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/TimedOutputAdapter.php',
|
||||
'Phinx\\Db\\Adapter\\UnsupportedColumnTypeException' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/UnsupportedColumnTypeException.php',
|
||||
'Phinx\\Db\\Adapter\\WrapperInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Adapter/WrapperInterface.php',
|
||||
'Phinx\\Db\\Plan\\AlterTable' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Plan/AlterTable.php',
|
||||
'Phinx\\Db\\Plan\\Intent' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Plan/Intent.php',
|
||||
'Phinx\\Db\\Plan\\NewTable' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Plan/NewTable.php',
|
||||
'Phinx\\Db\\Plan\\Plan' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Plan/Plan.php',
|
||||
'Phinx\\Db\\Plan\\Solver\\ActionSplitter' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Plan/Solver/ActionSplitter.php',
|
||||
'Phinx\\Db\\Table' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table.php',
|
||||
'Phinx\\Db\\Table\\Column' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/Column.php',
|
||||
'Phinx\\Db\\Table\\ForeignKey' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/ForeignKey.php',
|
||||
'Phinx\\Db\\Table\\Index' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/Index.php',
|
||||
'Phinx\\Db\\Table\\Table' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Table/Table.php',
|
||||
'Phinx\\Db\\Util\\AlterInstructions' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Db/Util/AlterInstructions.php',
|
||||
'Phinx\\Migration\\AbstractMigration' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/AbstractMigration.php',
|
||||
'Phinx\\Migration\\AbstractTemplateCreation' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/AbstractTemplateCreation.php',
|
||||
'Phinx\\Migration\\CreationInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/CreationInterface.php',
|
||||
'Phinx\\Migration\\IrreversibleMigrationException' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/IrreversibleMigrationException.php',
|
||||
'Phinx\\Migration\\Manager' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/Manager.php',
|
||||
'Phinx\\Migration\\Manager\\Environment' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/Manager/Environment.php',
|
||||
'Phinx\\Migration\\MigrationInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Migration/MigrationInterface.php',
|
||||
'Phinx\\Seed\\AbstractSeed' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Seed/AbstractSeed.php',
|
||||
'Phinx\\Seed\\SeedInterface' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Seed/SeedInterface.php',
|
||||
'Phinx\\Util\\Expression' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Util/Expression.php',
|
||||
'Phinx\\Util\\Literal' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Util/Literal.php',
|
||||
'Phinx\\Util\\Util' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Util/Util.php',
|
||||
'Phinx\\Wrapper\\TextWrapper' => __DIR__ . '/..' . '/robmorgan/phinx/src/Phinx/Wrapper/TextWrapper.php',
|
||||
'PhpOffice\\PhpSpreadsheet\\Calculation\\ArrayEnabled' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/ArrayEnabled.php',
|
||||
'PhpOffice\\PhpSpreadsheet\\Calculation\\BinaryComparison' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/BinaryComparison.php',
|
||||
'PhpOffice\\PhpSpreadsheet\\Calculation\\Calculation' => __DIR__ . '/..' . '/phpoffice/phpspreadsheet/src/PhpSpreadsheet/Calculation/Calculation.php',
|
||||
@@ -7477,6 +7833,96 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Symfony\\Component\\Clock\\MonotonicClock' => __DIR__ . '/..' . '/symfony/clock/MonotonicClock.php',
|
||||
'Symfony\\Component\\Clock\\NativeClock' => __DIR__ . '/..' . '/symfony/clock/NativeClock.php',
|
||||
'Symfony\\Component\\Clock\\Test\\ClockSensitiveTrait' => __DIR__ . '/..' . '/symfony/clock/Test/ClockSensitiveTrait.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ClassBuilder' => __DIR__ . '/..' . '/symfony/config/Builder/ClassBuilder.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ConfigBuilderGenerator' => __DIR__ . '/..' . '/symfony/config/Builder/ConfigBuilderGenerator.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ConfigBuilderGeneratorInterface' => __DIR__ . '/..' . '/symfony/config/Builder/ConfigBuilderGeneratorInterface.php',
|
||||
'Symfony\\Component\\Config\\Builder\\ConfigBuilderInterface' => __DIR__ . '/..' . '/symfony/config/Builder/ConfigBuilderInterface.php',
|
||||
'Symfony\\Component\\Config\\Builder\\Method' => __DIR__ . '/..' . '/symfony/config/Builder/Method.php',
|
||||
'Symfony\\Component\\Config\\Builder\\Property' => __DIR__ . '/..' . '/symfony/config/Builder/Property.php',
|
||||
'Symfony\\Component\\Config\\ConfigCache' => __DIR__ . '/..' . '/symfony/config/ConfigCache.php',
|
||||
'Symfony\\Component\\Config\\ConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactory.php',
|
||||
'Symfony\\Component\\Config\\ConfigCacheFactoryInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheFactoryInterface.php',
|
||||
'Symfony\\Component\\Config\\ConfigCacheInterface' => __DIR__ . '/..' . '/symfony/config/ConfigCacheInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/ArrayNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ArrayShapeGenerator' => __DIR__ . '/..' . '/symfony/config/Definition/ArrayShapeGenerator.php',
|
||||
'Symfony\\Component\\Config\\Definition\\BaseNode' => __DIR__ . '/..' . '/symfony/config/Definition/BaseNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\BooleanNode' => __DIR__ . '/..' . '/symfony/config/Definition/BooleanNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ArrayNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ArrayNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\BooleanNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BooleanNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\BuilderAwareInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/BuilderAwareInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\EnumNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/EnumNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ExprBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ExprBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\FloatNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/FloatNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\IntegerNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/IntegerNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\MergeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/MergeBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NodeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NodeParentInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NodeParentInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NormalizationBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NormalizationBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\NumericNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/NumericNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ParentNodeDefinitionInterface' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ParentNodeDefinitionInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ScalarNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ScalarNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\StringNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/StringNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\TreeBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/TreeBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\ValidationBuilder' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/ValidationBuilder.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Builder\\VariableNodeDefinition' => __DIR__ . '/..' . '/symfony/config/Definition/Builder/VariableNodeDefinition.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ConfigurableInterface' => __DIR__ . '/..' . '/symfony/config/Definition/ConfigurableInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Configuration' => __DIR__ . '/..' . '/symfony/config/Definition/Configuration.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ConfigurationInterface' => __DIR__ . '/..' . '/symfony/config/Definition/ConfigurationInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Configurator\\DefinitionConfigurator' => __DIR__ . '/..' . '/symfony/config/Definition/Configurator/DefinitionConfigurator.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Dumper\\XmlReferenceDumper' => __DIR__ . '/..' . '/symfony/config/Definition/Dumper/XmlReferenceDumper.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Dumper\\YamlReferenceDumper' => __DIR__ . '/..' . '/symfony/config/Definition/Dumper/YamlReferenceDumper.php',
|
||||
'Symfony\\Component\\Config\\Definition\\EnumNode' => __DIR__ . '/..' . '/symfony/config/Definition/EnumNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\DuplicateKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/DuplicateKeyException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\Exception' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/Exception.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\ForbiddenOverwriteException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/ForbiddenOverwriteException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\InvalidConfigurationException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidConfigurationException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\InvalidDefinitionException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidDefinitionException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\InvalidTypeException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/InvalidTypeException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Exception\\UnsetKeyException' => __DIR__ . '/..' . '/symfony/config/Definition/Exception/UnsetKeyException.php',
|
||||
'Symfony\\Component\\Config\\Definition\\FloatNode' => __DIR__ . '/..' . '/symfony/config/Definition/FloatNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\IntegerNode' => __DIR__ . '/..' . '/symfony/config/Definition/IntegerNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Loader\\DefinitionFileLoader' => __DIR__ . '/..' . '/symfony/config/Definition/Loader/DefinitionFileLoader.php',
|
||||
'Symfony\\Component\\Config\\Definition\\NodeInterface' => __DIR__ . '/..' . '/symfony/config/Definition/NodeInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\NumericNode' => __DIR__ . '/..' . '/symfony/config/Definition/NumericNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\Processor' => __DIR__ . '/..' . '/symfony/config/Definition/Processor.php',
|
||||
'Symfony\\Component\\Config\\Definition\\PrototypeNodeInterface' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypeNodeInterface.php',
|
||||
'Symfony\\Component\\Config\\Definition\\PrototypedArrayNode' => __DIR__ . '/..' . '/symfony/config/Definition/PrototypedArrayNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\ScalarNode' => __DIR__ . '/..' . '/symfony/config/Definition/ScalarNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\StringNode' => __DIR__ . '/..' . '/symfony/config/Definition/StringNode.php',
|
||||
'Symfony\\Component\\Config\\Definition\\VariableNode' => __DIR__ . '/..' . '/symfony/config/Definition/VariableNode.php',
|
||||
'Symfony\\Component\\Config\\Exception\\FileLoaderImportCircularReferenceException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLoaderImportCircularReferenceException.php',
|
||||
'Symfony\\Component\\Config\\Exception\\FileLocatorFileNotFoundException' => __DIR__ . '/..' . '/symfony/config/Exception/FileLocatorFileNotFoundException.php',
|
||||
'Symfony\\Component\\Config\\Exception\\LoaderLoadException' => __DIR__ . '/..' . '/symfony/config/Exception/LoaderLoadException.php',
|
||||
'Symfony\\Component\\Config\\Exception\\LogicException' => __DIR__ . '/..' . '/symfony/config/Exception/LogicException.php',
|
||||
'Symfony\\Component\\Config\\FileLocator' => __DIR__ . '/..' . '/symfony/config/FileLocator.php',
|
||||
'Symfony\\Component\\Config\\FileLocatorInterface' => __DIR__ . '/..' . '/symfony/config/FileLocatorInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\DelegatingLoader' => __DIR__ . '/..' . '/symfony/config/Loader/DelegatingLoader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\DirectoryAwareLoaderInterface' => __DIR__ . '/..' . '/symfony/config/Loader/DirectoryAwareLoaderInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\FileLoader' => __DIR__ . '/..' . '/symfony/config/Loader/FileLoader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\GlobFileLoader' => __DIR__ . '/..' . '/symfony/config/Loader/GlobFileLoader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\Loader' => __DIR__ . '/..' . '/symfony/config/Loader/Loader.php',
|
||||
'Symfony\\Component\\Config\\Loader\\LoaderInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\LoaderResolver' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolver.php',
|
||||
'Symfony\\Component\\Config\\Loader\\LoaderResolverInterface' => __DIR__ . '/..' . '/symfony/config/Loader/LoaderResolverInterface.php',
|
||||
'Symfony\\Component\\Config\\Loader\\ParamConfigurator' => __DIR__ . '/..' . '/symfony/config/Loader/ParamConfigurator.php',
|
||||
'Symfony\\Component\\Config\\ResourceCheckerConfigCache' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCache.php',
|
||||
'Symfony\\Component\\Config\\ResourceCheckerConfigCacheFactory' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerConfigCacheFactory.php',
|
||||
'Symfony\\Component\\Config\\ResourceCheckerInterface' => __DIR__ . '/..' . '/symfony/config/ResourceCheckerInterface.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ClassExistenceResource' => __DIR__ . '/..' . '/symfony/config/Resource/ClassExistenceResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ComposerResource' => __DIR__ . '/..' . '/symfony/config/Resource/ComposerResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\DirectoryResource' => __DIR__ . '/..' . '/symfony/config/Resource/DirectoryResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\FileExistenceResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileExistenceResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\FileResource' => __DIR__ . '/..' . '/symfony/config/Resource/FileResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\GlobResource' => __DIR__ . '/..' . '/symfony/config/Resource/GlobResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ReflectionClassResource' => __DIR__ . '/..' . '/symfony/config/Resource/ReflectionClassResource.php',
|
||||
'Symfony\\Component\\Config\\Resource\\ResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/ResourceInterface.php',
|
||||
'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceChecker' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceChecker.php',
|
||||
'Symfony\\Component\\Config\\Resource\\SelfCheckingResourceInterface' => __DIR__ . '/..' . '/symfony/config/Resource/SelfCheckingResourceInterface.php',
|
||||
'Symfony\\Component\\Config\\Resource\\SkippingResourceChecker' => __DIR__ . '/..' . '/symfony/config/Resource/SkippingResourceChecker.php',
|
||||
'Symfony\\Component\\Config\\Util\\Exception\\InvalidXmlException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/InvalidXmlException.php',
|
||||
'Symfony\\Component\\Config\\Util\\Exception\\XmlParsingException' => __DIR__ . '/..' . '/symfony/config/Util/Exception/XmlParsingException.php',
|
||||
'Symfony\\Component\\Config\\Util\\XmlUtils' => __DIR__ . '/..' . '/symfony/config/Util/XmlUtils.php',
|
||||
'Symfony\\Component\\Console\\Application' => __DIR__ . '/..' . '/symfony/console/Application.php',
|
||||
'Symfony\\Component\\Console\\Attribute\\Argument' => __DIR__ . '/..' . '/symfony/console/Attribute/Argument.php',
|
||||
'Symfony\\Component\\Console\\Attribute\\AsCommand' => __DIR__ . '/..' . '/symfony/console/Attribute/AsCommand.php',
|
||||
@@ -7685,6 +8131,14 @@ class ComposerStaticInitc91cd9c5b1e6a9e8573a14b799ea9342
|
||||
'Symfony\\Component\\EventDispatcher\\EventSubscriberInterface' => __DIR__ . '/..' . '/symfony/event-dispatcher/EventSubscriberInterface.php',
|
||||
'Symfony\\Component\\EventDispatcher\\GenericEvent' => __DIR__ . '/..' . '/symfony/event-dispatcher/GenericEvent.php',
|
||||
'Symfony\\Component\\EventDispatcher\\ImmutableEventDispatcher' => __DIR__ . '/..' . '/symfony/event-dispatcher/ImmutableEventDispatcher.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\ExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/ExceptionInterface.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\FileNotFoundException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/FileNotFoundException.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\IOException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOException.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\IOExceptionInterface' => __DIR__ . '/..' . '/symfony/filesystem/Exception/IOExceptionInterface.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\InvalidArgumentException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/InvalidArgumentException.php',
|
||||
'Symfony\\Component\\Filesystem\\Exception\\RuntimeException' => __DIR__ . '/..' . '/symfony/filesystem/Exception/RuntimeException.php',
|
||||
'Symfony\\Component\\Filesystem\\Filesystem' => __DIR__ . '/..' . '/symfony/filesystem/Filesystem.php',
|
||||
'Symfony\\Component\\Filesystem\\Path' => __DIR__ . '/..' . '/symfony/filesystem/Path.php',
|
||||
'Symfony\\Component\\Finder\\Comparator\\Comparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/Comparator.php',
|
||||
'Symfony\\Component\\Finder\\Comparator\\DateComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/DateComparator.php',
|
||||
'Symfony\\Component\\Finder\\Comparator\\NumberComparator' => __DIR__ . '/..' . '/symfony/finder/Comparator/NumberComparator.php',
|
||||
|
||||
Vendored
+671
@@ -593,6 +593,345 @@
|
||||
],
|
||||
"install-path": "../brick/math"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/chronos",
|
||||
"version": "3.5.0",
|
||||
"version_normalized": "3.5.0.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"
|
||||
},
|
||||
"time": "2026-04-10T02:50:39+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
},
|
||||
"install-path": "../cakephp/chronos"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/core",
|
||||
"version": "5.3.6",
|
||||
"version_normalized": "5.3.6.0",
|
||||
"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"
|
||||
},
|
||||
"time": "2026-05-15T03:31:14+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
},
|
||||
"install-path": "../cakephp/core"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/database",
|
||||
"version": "5.3.6",
|
||||
"version_normalized": "5.3.6.0",
|
||||
"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."
|
||||
},
|
||||
"time": "2026-05-21T19:38:13+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
},
|
||||
"install-path": "../cakephp/database"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/datasource",
|
||||
"version": "5.3.6",
|
||||
"version_normalized": "5.3.6.0",
|
||||
"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."
|
||||
},
|
||||
"time": "2026-05-20T10:27:33+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
},
|
||||
"install-path": "../cakephp/datasource"
|
||||
},
|
||||
{
|
||||
"name": "cakephp/utility",
|
||||
"version": "5.3.6",
|
||||
"version_normalized": "5.3.6.0",
|
||||
"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()"
|
||||
},
|
||||
"time": "2026-05-21T19:38:13+00:00",
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-5.next": "5.4.x-dev"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
},
|
||||
"install-path": "../cakephp/utility"
|
||||
},
|
||||
{
|
||||
"name": "carbonphp/carbon-doctrine-types",
|
||||
"version": "3.2.0",
|
||||
@@ -3640,6 +3979,93 @@
|
||||
],
|
||||
"install-path": "../league/config"
|
||||
},
|
||||
{
|
||||
"name": "league/container",
|
||||
"version": "5.2.0",
|
||||
"version_normalized": "5.2.0.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"
|
||||
},
|
||||
"time": "2026-03-19T18:52:39+00:00",
|
||||
"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"
|
||||
}
|
||||
},
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"install-path": "../league/container"
|
||||
},
|
||||
{
|
||||
"name": "league/flysystem",
|
||||
"version": "3.28.0",
|
||||
@@ -7146,6 +7572,96 @@
|
||||
],
|
||||
"install-path": "../ramsey/uuid"
|
||||
},
|
||||
{
|
||||
"name": "robmorgan/phinx",
|
||||
"version": "0.16.12",
|
||||
"version_normalized": "0.16.12.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/cakephp/phinx.git",
|
||||
"reference": "1cce44387a9146dc04c312e6a231b17fb30385cd"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/cakephp/phinx/zipball/1cce44387a9146dc04c312e6a231b17fb30385cd",
|
||||
"reference": "1cce44387a9146dc04c312e6a231b17fb30385cd",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"cakephp/database": "^5.0.2",
|
||||
"composer-runtime-api": "^2.0",
|
||||
"php-64bit": ">=8.1",
|
||||
"psr/container": "^1.1|^2.0",
|
||||
"symfony/config": "^4.0|^5.0|^6.0|^7.0|^8.0",
|
||||
"symfony/console": "^6.0|^7.0|^8.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"cakephp/cakephp-codesniffer": "^5.0",
|
||||
"cakephp/i18n": "^5.0",
|
||||
"ext-json": "*",
|
||||
"ext-pdo": "*",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"symfony/yaml": "^4.0|^5.0|^6.0|^7.0|^8.0"
|
||||
},
|
||||
"suggest": {
|
||||
"ext-json": "Install if using JSON configuration format",
|
||||
"ext-pdo": "PDO extension is needed",
|
||||
"symfony/yaml": "Install if using YAML configuration format"
|
||||
},
|
||||
"time": "2026-07-03T07:27:17+00:00",
|
||||
"bin": [
|
||||
"bin/phinx"
|
||||
],
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Phinx\\": "src/Phinx/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Rob Morgan",
|
||||
"email": "robbym@gmail.com",
|
||||
"homepage": "https://robmorgan.id.au",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Woody Gilk",
|
||||
"email": "woody.gilk@gmail.com",
|
||||
"homepage": "https://shadowhand.me",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "Richard Quadling",
|
||||
"email": "rquadling@gmail.com",
|
||||
"role": "Developer"
|
||||
},
|
||||
{
|
||||
"name": "CakePHP Community",
|
||||
"homepage": "https://github.com/cakephp/phinx/graphs/contributors",
|
||||
"role": "Developer"
|
||||
}
|
||||
],
|
||||
"description": "Phinx makes it ridiculously easy to manage the database migrations for your PHP app.",
|
||||
"homepage": "https://phinx.org",
|
||||
"keywords": [
|
||||
"database",
|
||||
"database migrations",
|
||||
"db",
|
||||
"migrations",
|
||||
"phinx"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/cakephp/phinx/issues",
|
||||
"source": "https://github.com/cakephp/phinx/tree/0.16.12"
|
||||
},
|
||||
"install-path": "../robmorgan/phinx"
|
||||
},
|
||||
{
|
||||
"name": "sebastian/cli-parser",
|
||||
"version": "2.0.1",
|
||||
@@ -8849,6 +9365,88 @@
|
||||
],
|
||||
"install-path": "../symfony/clock"
|
||||
},
|
||||
{
|
||||
"name": "symfony/config",
|
||||
"version": "v7.4.14",
|
||||
"version_normalized": "7.4.14.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/config.git",
|
||||
"reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/config/zipball/7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
|
||||
"reference": "7b665e443381ea7c4db03eb03b4bf79ea2b020eb",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=8.2",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/filesystem": "^7.1|^8.0",
|
||||
"symfony/polyfill-ctype": "~1.8"
|
||||
},
|
||||
"conflict": {
|
||||
"symfony/finder": "<6.4",
|
||||
"symfony/service-contracts": "<2.5"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/event-dispatcher": "^6.4|^7.0|^8.0",
|
||||
"symfony/finder": "^6.4|^7.0|^8.0",
|
||||
"symfony/messenger": "^6.4|^7.0|^8.0",
|
||||
"symfony/service-contracts": "^2.5|^3",
|
||||
"symfony/yaml": "^6.4|^7.0|^8.0"
|
||||
},
|
||||
"time": "2026-06-09T07:51:57+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Symfony\\Component\\Config\\": ""
|
||||
},
|
||||
"exclude-from-classmap": [
|
||||
"/Tests/"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "Helps you find, load, combine, autofill and validate configuration values of any kind",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/config/tree/v7.4.14"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/nicolas-grekas",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"install-path": "../symfony/config"
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.1.3",
|
||||
@@ -9323,6 +9921,79 @@
|
||||
],
|
||||
"install-path": "../symfony/event-dispatcher-contracts"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
"version": "v7.4.11",
|
||||
"version_normalized": "7.4.11.0",
|
||||
"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"
|
||||
},
|
||||
"time": "2026-05-11T16:38:44+00:00",
|
||||
"type": "library",
|
||||
"installation-source": "dist",
|
||||
"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"
|
||||
}
|
||||
],
|
||||
"install-path": "../symfony/filesystem"
|
||||
},
|
||||
{
|
||||
"name": "symfony/finder",
|
||||
"version": "v7.1.3",
|
||||
|
||||
Vendored
+91
-2
@@ -3,7 +3,7 @@
|
||||
'name' => 'loshmis/vanguard',
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => 'baf3f6da32fd5bc669ac7360970be21407d1d384',
|
||||
'reference' => 'b19963808e5545f0b99bf0eba214b0c85b2f4b7b',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -82,6 +82,51 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'cakephp/chronos' => array(
|
||||
'pretty_version' => '3.5.0',
|
||||
'version' => '3.5.0.0',
|
||||
'reference' => 'e6e777b534244911566face8a5dbdbd7f7bda5a6',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../cakephp/chronos',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'cakephp/core' => array(
|
||||
'pretty_version' => '5.3.6',
|
||||
'version' => '5.3.6.0',
|
||||
'reference' => '9c458b0e9322ec88bc4c758b33cde6a0abf49d12',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../cakephp/core',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'cakephp/database' => array(
|
||||
'pretty_version' => '5.3.6',
|
||||
'version' => '5.3.6.0',
|
||||
'reference' => '442d12bf0a1edeffc555a59de9f955cb0619c7ca',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../cakephp/database',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'cakephp/datasource' => array(
|
||||
'pretty_version' => '5.3.6',
|
||||
'version' => '5.3.6.0',
|
||||
'reference' => 'b768f0c0bf0fca815f83c4caef14e7fbb2409bf5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../cakephp/datasource',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'cakephp/utility' => array(
|
||||
'pretty_version' => '5.3.6',
|
||||
'version' => '5.3.6.0',
|
||||
'reference' => '4c703a010b9d955fed44731669e35d3043425cc7',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../cakephp/utility',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'carbonphp/carbon-doctrine-types' => array(
|
||||
'pretty_version' => '3.2.0',
|
||||
'version' => '3.2.0.0',
|
||||
@@ -649,6 +694,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/container' => array(
|
||||
'pretty_version' => '5.2.0',
|
||||
'version' => '5.2.0.0',
|
||||
'reference' => '58accbc032f0090a9bd08326f93062c5a658b2c5',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../league/container',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'league/flysystem' => array(
|
||||
'pretty_version' => '3.28.0',
|
||||
'version' => '3.28.0.0',
|
||||
@@ -688,7 +742,7 @@
|
||||
'loshmis/vanguard' => array(
|
||||
'pretty_version' => 'dev-main',
|
||||
'version' => 'dev-main',
|
||||
'reference' => 'baf3f6da32fd5bc669ac7360970be21407d1d384',
|
||||
'reference' => 'b19963808e5545f0b99bf0eba214b0c85b2f4b7b',
|
||||
'type' => 'project',
|
||||
'install_path' => __DIR__ . '/../../',
|
||||
'aliases' => array(),
|
||||
@@ -826,6 +880,12 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'orno/di' => array(
|
||||
'dev_requirement' => false,
|
||||
'replaced' => array(
|
||||
0 => '~2.0',
|
||||
),
|
||||
),
|
||||
'paragonie/constant_time_encoding' => array(
|
||||
'pretty_version' => 'v2.7.0',
|
||||
'version' => '2.7.0.0',
|
||||
@@ -1025,6 +1085,8 @@
|
||||
'dev_requirement' => false,
|
||||
'provided' => array(
|
||||
0 => '1.1|2.0',
|
||||
1 => '^1.0',
|
||||
2 => '^2.0',
|
||||
),
|
||||
),
|
||||
'psr/event-dispatcher' => array(
|
||||
@@ -1160,6 +1222,15 @@
|
||||
0 => '4.7.6',
|
||||
),
|
||||
),
|
||||
'robmorgan/phinx' => array(
|
||||
'pretty_version' => '0.16.12',
|
||||
'version' => '0.16.12.0',
|
||||
'reference' => '1cce44387a9146dc04c312e6a231b17fb30385cd',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../robmorgan/phinx',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'sebastian/cli-parser' => array(
|
||||
'pretty_version' => '2.0.1',
|
||||
'version' => '2.0.1.0',
|
||||
@@ -1391,6 +1462,15 @@
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/config' => array(
|
||||
'pretty_version' => 'v7.4.14',
|
||||
'version' => '7.4.14.0',
|
||||
'reference' => '7b665e443381ea7c4db03eb03b4bf79ea2b020eb',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/config',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/console' => array(
|
||||
'pretty_version' => 'v7.1.3',
|
||||
'version' => '7.1.3.0',
|
||||
@@ -1451,6 +1531,15 @@
|
||||
0 => '2.0|3.0',
|
||||
),
|
||||
),
|
||||
'symfony/filesystem' => array(
|
||||
'pretty_version' => 'v7.4.11',
|
||||
'version' => '7.4.11.0',
|
||||
'reference' => 'd721ea61b4a5fba8c5b6e7c1feda19efea144b50',
|
||||
'type' => 'library',
|
||||
'install_path' => __DIR__ . '/../symfony/filesystem',
|
||||
'aliases' => array(),
|
||||
'dev_requirement' => false,
|
||||
),
|
||||
'symfony/finder' => array(
|
||||
'pretty_version' => 'v7.1.3',
|
||||
'version' => '7.1.3.0',
|
||||
|
||||
Vendored
+2
-3
@@ -23,8 +23,7 @@ if ($issues) {
|
||||
echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL;
|
||||
}
|
||||
}
|
||||
trigger_error(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues),
|
||||
E_USER_ERROR
|
||||
throw new \RuntimeException(
|
||||
'Composer detected issues in your platform: ' . implode(' ', $issues)
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user