Compare commits

...

266 Commits

Author SHA1 Message Date
solocla dab8d9aebf fixed mapping for json 2026-06-08 08:47:17 +02:00
solocla 375a10a678 filter analysis web 2026-06-08 07:40:05 +02:00
solocla 15990be884 fxied column order LIMS 2026-06-04 16:48:22 +02:00
RMubarakzyanov c3a6dd73b6 import backoff 2026-05-28 23:59:15 +03:00
solocla 44ed1186e0 added richmento pelletteria routine 2026-05-28 13:50:52 +02:00
solocla 9050cb1006 routine burberry 2026-05-26 12:15:45 +02:00
solocla e6820fdb62 added order column 2026-05-25 10:59:58 +02:00
solocla 5da37a7836 paulshark routine 2026-05-22 12:27:53 +02:00
solocla c5f27cb69a routine fendi 2026-05-21 10:11:53 +02:00
solocla 1d81d6c996 fixed import update 2026-05-20 18:47:41 +02:00
solocla 0c72dbf5ae sort first with exactly phrase 2026-05-20 17:01:07 +02:00
solocla 8455be04e1 remove hidden from xls import to avoid big file dimension 2026-05-20 16:47:18 +02:00
solocla e42d1b9c51 fix limit dropdown 2026-05-19 16:20:55 +02:00
solocla 3e69e3c322 fixed routine valentino 2026-05-19 11:23:26 +02:00
solocla 0eb4f7a2ad valentino routine 2026-05-19 11:12:18 +02:00
solocla 4f2cfc1930 fix render gird for doppi apici 2026-05-18 15:38:43 +02:00
solocla df075dd76a main field show in imported 2026-05-14 10:14:59 +02:00
solocla 6460454201 fixed main field 2026-05-14 10:00:47 +02:00
solocla 574ddbbd32 fixed propagation 2026-05-13 15:12:13 +02:00
solocla 41f414db5c 2 main 2026-05-13 14:58:13 +02:00
solocla 4a863e8c16 fixed double scroll bar parts dropdown 2026-05-13 14:26:11 +02:00
solocla b431f1d4e9 added top scrollbar imported 2026-05-13 14:22:43 +02:00
solocla f97b52f158 skip empty parts during clone and LIMS export 2026-05-13 11:54:28 +02:00
solocla 836fc055ec cliente reponsabile change based on client 2026-05-12 15:48:35 +02:00
solocla e8dd585df4 added arrows to jump parts 2026-05-12 09:37:52 +02:00
solocla 198b8c08ad Revert "update scheme different obbligatorioweb"
This reverts commit a3eb0f0a57.
2026-05-11 14:47:42 +02:00
solocla 28c467d55e fixed order import 2026-05-11 14:24:35 +02:00
solocla 56eee99a67 change template and import o use also sheet number of XLS 2026-05-09 15:39:43 +02:00
solocla f514b3d2c7 added sheet and config API in insert edit template 2026-05-09 09:44:52 +02:00
solocla a3eb0f0a57 update scheme different obbligatorioweb 2026-05-09 09:30:59 +02:00
solocla aa355905d7 update schema 2026-05-08 11:10:21 +02:00
solocla b38f3e1240 Stop tracking generated schema response file 2026-05-06 16:52:53 +02:00
solocla cbd0c5b68a annotation color and clone templates 2026-05-06 16:44:42 +02:00
solocla f3e5cb4ffd fixed capitolato riferimento 2026-05-05 09:37:18 +02:00
solocla 0f0c6a04b7 fixed Mix 2026-05-05 09:07:14 +02:00
solocla 813bd66f96 scroll table parts 2026-05-04 12:10:12 +02:00
solocla ce00247d1c edit templtae lingue e colori 2026-05-04 11:21:30 +02:00
solocla 3a7dd266c8 fixed quotation 2026-04-29 12:16:44 +02:00
solocla ba8dc4c721 added get 2026-04-29 08:38:16 +02:00
RMubarakzyanov cfbbc36116 Fix TZ Issue 2026-04-28 12:53:21 +03:00
solocla 50d578eea1 commesssa on commessaweb 2026-04-27 16:02:50 +02:00
solocla 6b0d2aa9b9 fixed color and import dahboard 2026-04-27 14:34:45 +02:00
solocla fa7997c980 tested component fixed 2026-04-19 17:38:35 +02:00
solocla 66be442eb6 scrollbar import page 2026-04-17 15:00:17 +02:00
solocla 19a2d6e3f7 scrollbar also top 2026-04-17 10:47:41 +02:00
solocla a15ab08576 Merge branch 'main' of http://192.168.1.93:8418/solocla/trf_certest 2026-04-17 08:43:32 +02:00
solocla f71e8a56b5 cloneparts 2026-04-17 08:43:05 +02:00
RMubarakzyanov cb38bfb75a fix resize on tolims page 2026-04-16 11:39:00 +03:00
solocla 28a708dad3 removed limit admin 2026-04-15 16:19:39 +02:00
solocla 6b9cf20ab9 fixed resize and analysis 2026-04-15 14:37:49 +02:00
solocla d40fc7d177 update gitignore 2026-04-15 08:49:06 +02:00
RMubarakzyanov b812563023 Merge branch 'main' into feature/milestone3 2026-04-08 09:24:30 +03:00
solocla 39a821357e analysis complete 2026-04-03 11:15:33 +02:00
RMubarakzyanov 53c223ea5f merge 2026-04-02 17:01:22 +03:00
RMubarakzyanov 9775a12d4a Merge branch 'main' into feature/milestone3
# Conflicts:
#	public/userarea/import_insert.php
#	public/userarea/mapping_template_xls_scheme2.php
#	public/userarea/process_import_xls2.php
2026-04-02 12:29:17 +03:00
solocla 7dfb935e33 new mocnler routine (3 steps) 2026-04-02 09:49:09 +02:00
RMubarakzyanov abb4200215 Fix import 2026-03-31 17:15:43 +03:00
RMubarakzyanov 0be7109df4 Fix header finder 2026-03-31 16:33:08 +03:00
solocla 578671e013 added analysis by client and matrici 2026-03-31 14:08:51 +02:00
RMubarakzyanov d24836e2b1 matrici cache 2026-03-31 14:25:10 +03:00
RMubarakzyanov d983659000 transfer auto-detect header 2026-03-31 13:41:31 +03:00
RMubarakzyanov 7c5aa7734f hide form 2026-03-30 18:19:16 +03:00
RMubarakzyanov 4f0dbc7e91 cache and view improvements 2026-03-30 16:28:20 +03:00
RMubarakzyanov fb09f033ae removed htmlspecialchars on import && savings 2026-03-30 13:24:18 +03:00
solocla 8bae2d7008 analysis 2026-03-30 11:31:25 +02:00
RMubarakzyanov 7463fc6726 change links 2026-03-30 08:53:14 +03:00
RMubarakzyanov 0bc2ff7e9d fix autodetect 2026-03-29 21:47:23 +03:00
RMubarakzyanov aa1c32b7ed autodetect mapping 2026-03-29 21:07:16 +03:00
RMubarakzyanov c573a46318 tolims filter, pagination 2026-03-29 17:13:54 +03:00
solocla bf18a904bd update API template 2026-03-28 10:33:28 +01:00
solocla b3ce489348 api change dahsboard 2026-03-28 10:28:25 +01:00
solocla 08b89e01cc Stop tracking customfield_values_response.json 2026-03-28 10:28:08 +01:00
RMubarakzyanov 3e66d67dc5 remove htmlspecialchars from saving 2026-03-27 20:55:54 +03:00
solocla 490786731a moncler supplier fabric routine 2026-03-27 15:54:22 +01:00
solocla e96f538a47 added multiple column option template with color 2026-03-27 15:36:57 +01:00
solocla cc96ecb67f order parts by part_number 2026-03-27 15:18:44 +01:00
solocla 4cf03ae742 fixed routine 2026-03-27 09:36:06 +01:00
solocla 7a486e9dcf update moncler routine 2026-03-27 09:24:46 +01:00
RMubarakzyanov 2a7b1fae17 imported && tolims 2026-03-26 16:05:17 +03:00
solocla a05d9fed2b scheme alphabetical order 2026-03-26 11:47:02 +01:00
solocla 65170a0a7c fixed 285 export to LIMS and header import 2026-03-26 11:42:52 +01:00
solocla 223688c372 added select 2 on capitolato 2026-03-25 14:23:44 +01:00
solocla b562eb4033 fixed ordine alfabetico capitolato 2026-03-25 14:20:45 +01:00
solocla 0645a0c675 update profile 2026-03-25 11:02:27 +01:00
solocla a02a6b2c4c added clienteAnalisi on query export 2026-03-22 15:11:00 +01:00
RMubarakzyanov 45dd8d6907 Link photos to .01 Campioni 2026-03-21 16:18:27 +03:00
RMubarakzyanov 755f6812d4 Campione custom fields (Tested Component + parts), per-part ConsegnaRichiesta with validation 2026-03-20 23:31:31 +03:00
RMubarakzyanov 5e59ae2162 Tested component 2026-03-20 23:15:50 +03:00
solocla 71a19144c8 gitignore fixed 2026-03-19 13:48:51 +01:00
RMubarakzyanov 381a05341b PrimaPagina, StampaNelRapporto flags handling 2026-03-19 13:02:13 +03:00
solocla 5a58decd40 added clienti in template, export 2026-03-19 10:08:29 +01:00
RMubarakzyanov eb21910ef3 per-row buttons locking, progress 2026-03-18 19:58:57 +03:00
RMubarakzyanov 5dedc779df format date to d/m/Y 2026-03-18 19:24:21 +03:00
solocla f4e0074a73 fixed tested components 2026-03-18 16:11:03 +01:00
solocla 9c850e4ea6 fixed filter import 2026-03-18 16:07:25 +01:00
solocla f300811341 photos fixed background 2026-03-18 15:53:46 +01:00
RMubarakzyanov 48387a9945 cliente fornitore 2026-03-17 15:43:12 +03:00
RMubarakzyanov 0e90db8219 import date format 2026-03-17 12:44:37 +03:00
RMubarakzyanov eaf70d5a46 export date format 2026-03-16 20:57:05 +03:00
RMubarakzyanov cdd6551e9c remove validation simulate 2026-03-14 19:09:17 +03:00
RMubarakzyanov 1b97bf4362 1,2,3,4,5,6 points of milestone 2026-03-13 00:33:55 +03:00
solocla c516589483 changed notes TRF 2026-03-12 10:28:58 +01:00
solocla 817bbadf22 added flag to photos 2026-03-12 10:20:22 +01:00
solocla 1f27bc48d4 added get utenti 2026-03-11 14:53:38 +01:00
RMubarakzyanov c9fba48d88 fix post data 2026-03-11 16:04:37 +03:00
RMubarakzyanov 70d4c0759e importpayload logging 2026-03-09 17:34:32 +03:00
RMubarakzyanov 5d7880160a send userid to import 2026-03-09 17:12:38 +03:00
RMubarakzyanov bbe74d1529 fetch changes from branch export_to_lims 2026-03-09 17:07:20 +03:00
RMubarakzyanov 93930227a2 fetch changes from branch export_to_lims 2026-03-09 17:04:03 +03:00
solocla 2598a4c91b Step 9 remove comment on export to lims 2026-03-06 09:15:30 +01:00
solocla 5e677a8b9c Merge branch 'feature/export-to-lims' 2026-03-06 08:48:57 +01:00
solocla 540c44d89a fixed column mappings 2026-03-05 14:19:49 +01:00
solocla 2ee9f2ecb1 added proceed on the top 2026-03-04 10:13:33 +01:00
solocla c9122774b1 added additional field in parts template e layout 2026-03-02 14:37:20 +01:00
RMubarakzyanov 1fed113c5c VisualLimsApiClientMock: fake data for all LIMS endpoints; getInstance() branches on SIMULATE_EXPORT_LIMS
get_clienti.php, get_fixed_field_data.php: simulate mode support
CustomField dropdown values mock added (get_customfield_values.php)

exportUnsavedModal: prompt save before export, MutationObserver waits for save, then proceeds to confirm
Removed old jQuery .export-lims-btn handler that bypassed confirm modal
Fix false "Unsaved changes" on page load: data-restoring guard in all programmatic trigger/dispatchEvent calls (populateSelect, populateClientDropdowns, populateDropdowns)

Fix ConsegnaRichiesta not shown on refresh: add to PHP $fixedAliasMap
Add step5_2_photos, step9_1_importa
2026-02-28 00:44:21 +03:00
RMubarakzyanov e3994d6f9f fix notices running on php-8.2 2026-02-26 22:59:41 +03:00
solocla 9af0df3cca env example update 2026-02-26 17:14:16 +01:00
solocla 48d2a3ff42 time and date setting 2026-02-25 22:07:44 +01:00
solocla cf44e67922 added auto user accettatore id 2026-02-25 15:24:41 +01:00
solocla 35021e9d9b dropdown in alphabetical order 2026-02-25 10:02:45 +01:00
solocla 497ebda65a update gitignore 2026-02-24 09:09:32 +01:00
solocla b0024edb70 mapping fixed 2026-02-24 09:06:45 +01:00
solocla b9852ba226 saved rows 2026-02-19 12:07:32 +01:00
solocla 407d6884a1 fixed note save and import special character 2026-02-19 11:55:24 +01:00
solocla 1a4beadbb2 fixed propagate dropdown 2026-02-18 11:52:26 +01:00
solocla 4bb0445cff added select 2 on all fields (with limit of 12) 2026-02-18 10:48:04 +01:00
solocla 73dd8f4ce8 fixed column size final 2026-02-18 10:04:27 +01:00
solocla bdc4e0e60c fixed size column 2026-02-18 09:58:42 +01:00
solocla ef8f2d8000 fixed labels 2026-02-18 09:54:21 +01:00
solocla 74c9a1d02a added time field 2026-02-18 09:34:26 +01:00
solocla cd3bccd183 added multiple tested components 2026-02-17 09:04:30 +01:00
solocla 78154e43a9 fixed tested components 2026-02-05 10:32:28 +01:00
solocla 9fe9243e60 fixed mapping template 2026-02-05 09:05:51 +01:00
solocla a8330d4aba fixed layout 2026-02-04 15:31:53 +01:00
MGrigoryan 82d6a2ee18 feat(import_edit2): expand entire column including header and fix sticky columns 2026-02-04 17:00:01 +04:00
MGrigoryan d8eddb3aa5 feat(import_edit2): enhance grid UI with sticky columns and textarea conversion
- Fix grid-top alignment and datepicker positioning
- Extend column resizer to grid-top cells
- Add sticky columns for Actions and Sample Description
- Implement input-to-textarea conversion on focus
- Increase Select2 dropdown height
- Improve grid-container CSS for sticky support
2026-02-04 11:59:59 +04:00
solocla f60dc64b2d fixed importedit 2026-02-02 08:46:47 +01:00
solocla 4e4cae1df8 added fixed fields 2026-01-30 12:07:43 +01:00
solocla 8838edf3a1 fixed fields 2026-01-28 11:49:11 +01:00
solocla e75be99e43 added resort manually 2025-11-08 17:55:10 +01:00
solocla a482d975da Merge branch 'feature/parts-photo-load-from-quotation' 2025-10-31 10:38:28 +01:00
solocla 598a2cc84c change marker dimension 2025-10-31 10:16:57 +01:00
MGrigoryan 6ec0c2062e fix: Include single sample photo in save operation when no photos selected 2025-10-31 11:45:04 +04:00
MGrigoryan 5eb5bd1613 fix: deselect part when clicking selected row 2025-10-31 11:30:18 +04:00
MGrigoryan 03771e3ca8 feat: Add quotation modal and iddatadb update for quotation parts/images
- Add quotation modal for selecting quotation
- Load quotation parts and photos
- Update iddatadb with quotation parts and photo references
2025-10-31 11:26:11 +04:00
MGrigoryan 03642fdfab feat(annotations): support multiple pins per part 2025-10-30 17:08:57 +04:00
MGrigoryan f6ea17388c fix(partsTable, annotationsModal): correct table height and restore "Torna alle Parti"
- partsTable: reduce excessive height to prevent oversized rendering
- annotationsModal: fix "Torna alle Parti" button behavior to return to parts view
2025-10-30 10:26:46 +04:00
solocla 1c2b4ab7a6 Merge branch 'bugfix/part-creation-matrice-dropdown-fix' 2025-10-29 18:25:26 +01:00
MGrigoryan 31cb23b00e fix(partsTable): skip change handler when setting Select2 value programmatically
Pass skipHandler flag in change event data to prevent handler execution
during programmatic value updates.
2025-10-29 19:18:33 +04:00
MGrigoryan d29563d20d feat(partsTable): add image icon to show/hide photo button
- Add image icon alongside eye icon in show/hide photo button
- Improve visual indication of photo toggle functionality
2025-10-29 18:27:33 +04:00
solocla 82af925ac1 added size to line markers 2025-10-29 13:45:38 +01:00
solocla 5d8360dd87 marker with size 2025-10-29 12:32:09 +01:00
MGrigoryan 683073c244 fix(partsTable): initialize new item matrice as empty and refresh on global filter change
- Default new items' matrice to empty to prevent stale values leaking from prior state
- Force matrice update when the global filter changes to keep items in sync
2025-10-29 14:17:00 +04:00
solocla 8d6fe92481 fixed multiple parts mix and single line 2025-10-28 08:58:39 +01:00
solocla dbc66723a6 fixed color save 2025-10-27 15:53:12 +01:00
solocla 218fc14462 fixed country client and parts column matrice 2025-10-27 14:38:20 +01:00
solocla 29e4b41874 update export to lims 2025-10-11 20:19:43 +02:00
solocla eef9ae8d36 added note to export 2025-10-10 11:31:49 +02:00
solocla 68c867a3f4 change clienti to datadb and fixed column pages 2025-10-09 15:30:44 +02:00
solocla a9827e4e81 added note and date to identification parts 2025-10-08 17:34:21 +02:00
solocla b51936f784 various fixing modal 2025-10-07 20:56:57 +02:00
solocla 15b6f38e8b fixed matrici with db cron 2025-10-07 09:41:29 +02:00
solocla 12c6cc5f95 lazy load modal parts and matrici cron 2025-10-07 09:12:54 +02:00
solocla a0b12463c0 fixed for matrici 2025-10-03 08:52:37 +02:00
solocla 07ddcafd3f fixed nologin 2025-09-27 13:38:26 +02:00
solocla 7843d4b1fc added nologin 2025-09-27 13:37:37 +02:00
solocla 4eae855e23 added headscriptnologin 2025-09-27 13:36:26 +02:00
solocla c709f64a17 remove login smartphone 2025-09-27 13:33:23 +02:00
solocla d5f0690f59 background trasparent annotation 2025-09-27 13:28:21 +02:00
solocla 6bbd3fcae9 fixed get clienti with id, nominativo and country 2025-09-27 09:53:21 +02:00
solocla 9e19e9e1d4 fixed export to lims, fixed multiple upload, added calendar to Data 2025-09-27 09:44:00 +02:00
solocla 7caee9c994 prova 2025-09-26 11:27:33 +02:00
solocla f8320315f7 historical added commessaweb., edit added tested components 2025-09-26 09:28:49 +02:00
solocla 7397d86bc2 fixed template dashboard 2025-09-25 14:26:22 +02:00
solocla 2deb1f101a hide column and fixed edit template 2025-09-25 14:18:09 +02:00
solocla ed4467337f update moncler routine 2025-09-25 11:03:47 +02:00
solocla 864714d198 routine scripts 2025-09-24 14:18:31 +02:00
solocla 33aacfb469 env example 2025-09-23 17:39:44 +02:00
solocla e0e262fd32 Merge feature/lims-api into main, prefer lims-api version for conflicted files 2025-09-23 14:24:54 +02:00
solocla 5d6302fa9c added admin role to button export 2025-09-23 10:33:16 +02:00
solocla 3da8ff81c9 added record idcommessaweb and commessaweb and update status to export to lims 2025-09-23 09:44:58 +02:00
kapsona777 a36dd02771 Lims api working version without comment 2025-09-22 20:24:53 +04:00
kapsona777 0a6fb98476 Lims api working version with comments 2025-09-22 20:16:56 +04:00
solocla 57ddd4bb5a fixed photo quotations 2025-09-22 11:22:05 +02:00
solocla df5e6d5656 fixed save photos for modsecurity 2025-09-22 10:47:48 +02:00
solocla 78495880ca fixed quotations 2025-09-22 09:19:36 +02:00
solocla 960832efb1 Merge feature/quotations into main, prefer quotations version for modal_parts.php and parts.js 2025-09-22 08:24:05 +02:00
solocla 447a0d1dea fixed canvas dimension 2025-09-22 08:15:01 +02:00
solocla 5b47416841 fixed part.js 2025-09-22 07:59:13 +02:00
solocla 3e4a627ca7 fixed quotations 2025-09-20 22:00:16 +02:00
solocla 420b0a0405 added routine to insert templates 2025-09-19 12:15:41 +02:00
solocla b39d601ec9 Merge branch 'features/routine' 2025-09-19 12:10:01 +02:00
solocla 89d13699b4 added routine functionalites to template 2025-09-19 12:09:01 +02:00
solocla 9826331545 update 2025-09-19 11:45:21 +02:00
solocla 9d8718d110 only entraid provider 2025-09-19 09:50:57 +02:00
solocla 16e00f8573 entraid integration completa with docs 2025-09-19 09:31:20 +02:00
solocla baf3f6da32 photo size fixed 2025-09-18 17:02:38 +02:00
solocla 62bf4ebd92 export to lims 2025-09-18 12:00:43 +02:00
solocla 6e465e3010 fixed color parts 2025-09-17 09:41:51 +02:00
solocla 8b08969c69 get matrice 2025-09-16 11:39:44 +02:00
solocla 25d4519684 fixed quotations 2025-09-13 21:28:11 +02:00
solocla 34d4dc8660 quotations page 2025-09-13 12:31:31 +02:00
solocla 1510ef03f1 added level to collahge 2025-09-12 18:09:01 +02:00
solocla ce8c95921f remove .env from repository 2025-09-12 10:17:22 +02:00
solocla 095a6ae879 remove debug/log/json files from tracking 2025-09-12 10:03:50 +02:00
solocla 296143016a template mapping addes is visible as checkbox and is required as badge 2025-09-12 10:01:19 +02:00
kapsona777 412dce8941 refactored api code 2025-09-11 18:46:47 +04:00
kapsona777 586226ceaf fixed alert issue 2025-09-10 20:54:31 +04:00
kapsona777 ac09d8d0eb fixed issue, now works great. // ⚠️ Simulation ON (change it to false to enable real API calls)
$simulate = true;
2025-09-10 20:30:02 +04:00
kapsona777 33e3ae059d this commit is uploaded for testing only, no api calls are made 2025-09-09 17:28:48 +04:00
solocla 3aa2504f3c fixed cut 2025-09-08 14:01:17 +02:00
solocla c1a396f246 added canvas functionality 2025-09-08 11:40:43 +02:00
solocla a45ba1c8b3 added cut 2025-09-08 10:26:15 +02:00
solocla 7a944a73f7 get_commessaweb 2025-09-08 08:42:05 +02:00
solocla 71595cc8de added speech functions 2025-09-06 18:48:38 +02:00
solocla f89dbd0c23 added save all 2025-09-06 12:29:29 +02:00
solocla 9ba859e15b Merge remote-tracking branch 'origin/bugfix/warning-text-and-logic-change' 2025-09-05 21:44:38 +02:00
solocla 672e448e9a fixed renumerate parts 2025-09-04 15:16:19 +02:00
solocla 0749032fbc added collage, fixed parts marker color, added ri number 2025-09-04 15:01:03 +02:00
kapsona777 d692614f70 warning fix 2025-09-01 19:56:08 +04:00
solocla 1303cff9fd added export to lims button 2025-08-29 15:36:55 +02:00
solocla 6b2bd0964b fixed dropdown selection 2025-08-28 10:25:12 +02:00
solocla 0d2cf13524 update historical trf 2025-08-28 09:36:16 +02:00
solocla f6ef9c39d2 added multi webcam functionality 2025-08-27 20:21:38 +02:00
solocla 7e4ed56f28 Merge feature/historical_imported_trf into main (kept main version for logs and import_edit2.php) 2025-08-27 12:16:41 +02:00
solocla 06dd7883c2 update historical trf and navbar 2025-08-27 12:13:11 +02:00
solocla 4d0644f46c metada classes 2025-08-27 08:09:42 +02:00
solocla 712042b8d8 update layout table import edit 2 and fix address photos in env 2025-08-26 15:22:27 +02:00
solocla efee12740d fixed mappiung template 2025-08-26 11:54:03 +02:00
solocla 14395810d0 fixed template mapping 2025-08-26 11:49:08 +02:00
solocla 03002a8938 gitignore edit 2025-08-26 10:51:52 +02:00
solocla 21fcee8ff5 Merge feature/added-some-features into main (ignore debug log conflicts) 2025-08-26 10:46:27 +02:00
kapsona777 1361340928 fixed annotation description placement 2025-08-25 19:59:22 +04:00
kapsona777 1bda30e957 fixed saving and modifying parts 2025-08-25 19:39:53 +04:00
kapsona777 a87423d879 fixed saving and modifying parts 2025-08-25 18:08:33 +04:00
kapsona777 24cda34681 added feature to update inserted information, fixed bug that was inserting new rows after each refresh of the page and optimized get_customfield_values , it was sending many requests and optimized to 1. 2025-08-21 20:39:31 +04:00
solocla 22e4e652b5 import edit update 2025-08-21 12:41:30 +02:00
solocla 2c514a8ab6 added button on top page for historical 2025-08-21 11:38:23 +02:00
solocla b1ea728c15 added main field functionality 2025-08-21 09:27:21 +02:00
solocla caf5568779 git ignore edit 2025-08-20 17:37:22 +02:00
solocla 0728fd8f01 update main field and git ignore 2025-08-20 17:30:13 +02:00
kapsona777 9d5c20113f fixed arrows 2025-08-20 17:45:21 +04:00
kapsona777 47762a8557 added feature about unsaved changes 2025-08-20 16:49:10 +04:00
solocla 434bb0d993 added fill dropdown and save id 2025-08-19 16:33:11 +02:00
kapsona777 939a4fe03e added feature to save image into database and after upload it can be chose by dropdown 2025-08-18 19:45:38 +04:00
kapsona777 493de65892 fixed saving annotated photos 2025-08-18 19:02:57 +04:00
solocla d8eca66747 historical trf 2025-08-18 15:57:36 +02:00
kapsona777 23ae8e1b1d fixed displaying photo 2025-08-18 14:31:58 +04:00
solocla a14aa6eb98 gitignore 2025-08-18 09:14:19 +02:00
solocla 99a30e4d9f itignore update 2025-08-18 09:14:05 +02:00
kapsona777 7ad20993d9 added controller for QR photo upload and inserted route for it. added functional for multiple photo upload. 2025-08-11 16:30:24 +04:00
solocla 8978980901 logincontroller + select all + topbar 2025-07-31 15:19:56 +02:00
solocla 6d66c5cf97 mix part and loader 2025-07-30 15:43:14 +02:00
solocla b3f19be47d ignore updated 2025-07-30 11:03:34 +02:00
solocla 13e73abc5d fixed parts circle 2025-07-29 09:29:27 +02:00
solocla 14d91b6d6e fixed parts.js 2025-07-29 08:55:28 +02:00
solocla c004636b6c navbar mod e small details 2025-07-28 15:24:02 +02:00
solocla 4c4c6e3153 change link and color table 2025-07-09 16:45:31 +02:00
solocla 7d0824d01f fixed propagation 2025-07-09 10:33:01 +02:00
solocla 32c0966801 fixed import xls 2025-07-09 09:23:25 +02:00
solocla 57ab20ed1f added logs 2025-07-08 21:03:33 +02:00
solocla c533973420 update template and edit import 2025-07-08 12:25:47 +02:00
solocla b092abf8c7 new template schema 2025-07-07 11:39:56 +02:00
solocla 78089cadc1 added schema details 2025-07-05 20:31:44 +02:00
solocla 3816bf5a20 added get rapporto prova 2025-07-02 16:22:13 +02:00
solocla e8b15d8096 addition API BV 2025-06-05 10:11:57 +02:00
373 changed files with 164073 additions and 2042 deletions
-40
View File
@@ -1,40 +0,0 @@
APP_ENV=production
APP_DEBUG=true
APP_KEY=base64:C+sutHm6xP5sE4QXhoZFhYjArlVN11s2mDU1F8beUkM=
APP_URL=http://vanguard.test
LOG_CHANNEL=stack
DB_CONNECTION=mysql
DB_HOST="localhost"
DB_DATABASE="trfcertest"
DB_USERNAME="solocla"
DB_PASSWORD="!Massarosa2"
DB_PREFIX="auth_"
BROADCAST_DRIVER=log
CACHE_DRIVER=file
QUEUE_DRIVER=sync
SESSION_DRIVER=database
SESSION_LIFETIME=120
REDIS_HOST=127.0.0.1
REDIS_PASSWORD=null
REDIS_PORT=6379
MAIL_MAILER=mail
MAIL_FROM_NAME=Vanguard
MAIL_FROM_ADDRESS=vanguard@test.dev
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
PUSHER_APP_ID=
PUSHER_APP_KEY=
PUSHER_APP_SECRET=
PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
+14 -7
View File
@@ -1,16 +1,16 @@
APP_ENV=production APP_ENV=production
APP_DEBUG=false APP_DEBUG=true
APP_KEY= APP_KEY=base64:C+sutHm6xP5sE4QXhoZFhYjArlVN11s2mDU1F8beUkM=
APP_URL=http://vanguard.test APP_URL=http://vanguard.test
LOG_CHANNEL=stack LOG_CHANNEL=stack
DB_CONNECTION=mysql DB_CONNECTION=mysql
DB_HOST=localhost DB_HOST="localhost"
DB_DATABASE=vanguard DB_DATABASE="xxxx"
DB_USERNAME=homestead DB_USERNAME="xxxx"
DB_PASSWORD=secret DB_PASSWORD="xxxxx"
DB_PREFIX=vg_ DB_PREFIX="auth_"
BROADCAST_DRIVER=log BROADCAST_DRIVER=log
CACHE_DRIVER=file CACHE_DRIVER=file
@@ -39,3 +39,10 @@ PUSHER_APP_CLUSTER=mt1
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
# Credenziali API VisualLims
SIMULATE_EXPORT_LIMS=true
API_BASE_URL=https://bvcpsitaly-elims.com/limsapi
API_USERNAME=WebApiUser
API_PASSWORD=webapiuser01
BASE_URL=http://localhost:8000/userarea/
+52 -12
View File
@@ -1,23 +1,63 @@
.DS_Store .DS_Store
/node_modules /node_modules
/public/hot
/public/storage
/storage/*.key
/vendor /vendor
/.idea /.idea
/.fleet /.fleet
/.vscode /.vscode
/.vagrant /.vagrant
Homestead.json
Homestead.yaml /public/hot
npm-debug.log /public/storage
yarn-error.log
.env
.phpunit.result.cache
.php_cs.cache
/documentation
/.phpunit.cache
/public/build /public/build
/storage/*.key
.env
.env.backup .env.backup
.env.production .env.production
auth.json auth.json
.phpunit.result.cache
.php_cs.cache
/.phpunit.cache
npm-debug.log
yarn-error.log
/documentation
# --- Runtime / Debug (userarea) ---
/public/userarea/*.json
/public/userarea/*.log
/public/userarea/*.txt
/public/userarea/*_response.json
/public/userarea/customfield_values_response.json
/public/userarea/error_log.txt
/public/userarea/import_debug.log
/public/userarea/last_url.txt
/public/userarea/logaspi/
/public/userarea/logs/api/
/public/userarea/logs/api/**
/public/userarea/logs/
/public/userarea/logs/**
/public/userarea/photostrf/
/public/userarea/class/*.log
/public/userarea/class/curl_auth_debug.log
/public/userarea/class/curl_request_debug.log
/public/userarea/schema_dettagli_response.json
public/userarea/schemi_base_response.json
# File XLSX temporanei importati
/public/userarea/imported_trf/*.xlsx
/public/userarea/xlstemplates/*.xlsx
# Cartelle foto generate
/public/photostrf/
/public/photostrf/qrcodes/
# Ignora tutti i log ovunque
*.log
public/userarea/cache/
@@ -0,0 +1,74 @@
<?php
namespace App\Http\Controllers\Userarea;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
class UploadPhotosMobileController extends Controller
{
public function index(Request $request)
{
$iddatadb = $request->query('iddatadb');
if (empty($iddatadb)) {
return response('ID riga non fornito', 400);
}
// Show the upload form
return view('userarea.upload_photos_mobile', [
'iddatadb' => $iddatadb
]);
}
public function upload(Request $request)
{
// Validation
$request->validate([
'photo' => 'required|file|mimes:jpeg,png,gif,heic,heif|max:5120', // 5MB
'iddatadb' => 'required|integer'
]);
$iddatadb = $request->input('iddatadb');
$photo = $request->file('photo');
$iduserlogin = auth()->id(); // assuming Laravel authentication
// Check if user exists
$userExists = DB::table('auth_users')->where('id', $iduserlogin)->exists();
if (!$userExists) {
return response()->json(['success' => false, 'message' => 'Utente non valido']);
}
// Upload folder
$uploadDir = public_path('photostrf');
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
if (!is_writable($uploadDir)) {
return response()->json(['success' => false, 'message' => 'La cartella photostrf non è scrivibile']);
}
// New filename
$timestamp = now()->format('YmdHis');
$originalName = pathinfo($photo->getClientOriginalName(), PATHINFO_FILENAME);
$extension = strtolower($photo->getClientOriginalExtension());
$newFileName = "{$iddatadb}-{$timestamp}-{$originalName}.{$extension}";
$destination = $uploadDir . '/' . $newFileName;
// Move uploaded file
$photo->move($uploadDir, $newFileName);
// Save DB record
DB::table('datadb_photos')->insert([
'iddatadb' => $iddatadb,
'file_path' => $newFileName,
'file_name' => $newFileName,
'uploaded_by' => $iduserlogin
]);
return response()->json(['success' => true, 'message' => 'Foto caricata con successo']);
}
}
@@ -110,7 +110,7 @@ class LoginController extends Controller
if ($user->hasRole('Admin')) { if ($user->hasRole('Admin')) {
return redirect()->to('userarea/import_dashboard.php'); return redirect()->to('userarea/import_dashboard.php');
} elseif ($user->hasRole('User')) { } elseif ($user->hasRole('User')) {
return redirect()->to('userarea/index.php'); return redirect()->to('userarea/import_dashboard.php');
} }
// Se il ruolo non è specificato, reindirizza alla home predefinita // Se il ruolo non è specificato, reindirizza alla home predefinita
+12 -1
View File
@@ -28,10 +28,21 @@ class AppServiceProvider extends ServiceProvider
\Illuminate\Database\Schema\Builder::defaultStringLength(191); \Illuminate\Database\Schema\Builder::defaultStringLength(191);
Factory::guessFactoryNamesUsing(function (string $modelName) { Factory::guessFactoryNamesUsing(function (string $modelName) {
return 'Database\Factories\\'.class_basename($modelName).'Factory'; return 'Database\Factories\\' . class_basename($modelName) . 'Factory';
}); });
\Illuminate\Pagination\Paginator::useBootstrap(); \Illuminate\Pagination\Paginator::useBootstrap();
// Register Microsoft Socialite driver
$this->app->make('Laravel\Socialite\Contracts\Factory')->extend('microsoft', function ($app) {
$config = $app['config']['services.microsoft'];
return new \SocialiteProviders\Microsoft\Provider(
$app['request'],
$config['client_id'],
$config['client_secret'],
$config['redirect']
);
});
} }
/** /**
+3
View File
@@ -34,6 +34,9 @@ class EventServiceProvider extends ServiceProvider
Verified::class => [ Verified::class => [
ActivateUser::class, ActivateUser::class,
], ],
\SocialiteProviders\Manager\SocialiteWasMapped::class => [
\SocialiteProviders\Microsoft\MicrosoftExtendSocialite::class,
],
]; ];
/** /**
+1 -1
View File
@@ -12,7 +12,7 @@
*/ */
$app = new Illuminate\Foundation\Application( $app = new Illuminate\Foundation\Application(
realpath(__DIR__.'/../') realpath(__DIR__ . '/../')
); );
/* /*
+2 -1
View File
@@ -38,12 +38,13 @@
"laravel/fortify": "^1.21", "laravel/fortify": "^1.21",
"laravel/framework": "^11.0", "laravel/framework": "^11.0",
"laravel/sanctum": "^4.0", "laravel/sanctum": "^4.0",
"laravel/socialite": "^5.0", "laravel/socialite": "^5.16",
"laravel/tinker": "^2.7", "laravel/tinker": "^2.7",
"laravel/ui": "^4.0", "laravel/ui": "^4.0",
"phpmailer/phpmailer": "^6.9", "phpmailer/phpmailer": "^6.9",
"phpoffice/phpspreadsheet": "^4.1", "phpoffice/phpspreadsheet": "^4.1",
"proengsoft/laravel-jsvalidation": "^4.0.0", "proengsoft/laravel-jsvalidation": "^4.0.0",
"socialiteproviders/microsoft": "^4.7",
"spatie/laravel-query-builder": "^5.0", "spatie/laravel-query-builder": "^5.0",
"vanguardapp/activity-log": "^6.0", "vanguardapp/activity-log": "^6.0",
"vanguardapp/announcements": "^6.0", "vanguardapp/announcements": "^6.0",
Generated
+138 -13
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "ef3e05e7260284f5b7c7b4b6f93b252b", "content-hash": "9c4f1e3bc3ee2180211c055e70635aef",
"packages": [ "packages": [
{ {
"name": "akaunting/laravel-setting", "name": "akaunting/laravel-setting",
@@ -2240,16 +2240,16 @@
}, },
{ {
"name": "laravel/socialite", "name": "laravel/socialite",
"version": "v5.15.1", "version": "v5.16.0",
"source": { "source": {
"type": "git", "type": "git",
"url": "https://github.com/laravel/socialite.git", "url": "https://github.com/laravel/socialite.git",
"reference": "cc02625f0bd1f95dc3688eb041cce0f1e709d029" "reference": "40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf"
}, },
"dist": { "dist": {
"type": "zip", "type": "zip",
"url": "https://api.github.com/repos/laravel/socialite/zipball/cc02625f0bd1f95dc3688eb041cce0f1e709d029", "url": "https://api.github.com/repos/laravel/socialite/zipball/40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf",
"reference": "cc02625f0bd1f95dc3688eb041cce0f1e709d029", "reference": "40a2dc98c53d9dc6d55eadb0d490d3d72b73f1bf",
"shasum": "" "shasum": ""
}, },
"require": { "require": {
@@ -2271,16 +2271,16 @@
}, },
"type": "library", "type": "library",
"extra": { "extra": {
"branch-alias": {
"dev-master": "5.x-dev"
},
"laravel": { "laravel": {
"providers": [
"Laravel\\Socialite\\SocialiteServiceProvider"
],
"aliases": { "aliases": {
"Socialite": "Laravel\\Socialite\\Facades\\Socialite" "Socialite": "Laravel\\Socialite\\Facades\\Socialite"
} },
"providers": [
"Laravel\\Socialite\\SocialiteServiceProvider"
]
},
"branch-alias": {
"dev-master": "5.x-dev"
} }
}, },
"autoload": { "autoload": {
@@ -2308,7 +2308,7 @@
"issues": "https://github.com/laravel/socialite/issues", "issues": "https://github.com/laravel/socialite/issues",
"source": "https://github.com/laravel/socialite" "source": "https://github.com/laravel/socialite"
}, },
"time": "2024-06-28T20:09:34+00:00" "time": "2024-09-03T09:46:57+00:00"
}, },
{ {
"name": "laravel/tinker", "name": "laravel/tinker",
@@ -4980,6 +4980,131 @@
], ],
"time": "2024-04-27T21:32:50+00:00" "time": "2024-04-27T21:32:50+00:00"
}, },
{
"name": "socialiteproviders/manager",
"version": "v4.8.1",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Manager.git",
"reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SocialiteProviders/Manager/zipball/8180ec14bef230ec2351cff993d5d2d7ca470ef4",
"reference": "8180ec14bef230ec2351cff993d5d2d7ca470ef4",
"shasum": ""
},
"require": {
"illuminate/support": "^8.0 || ^9.0 || ^10.0 || ^11.0 || ^12.0",
"laravel/socialite": "^5.5",
"php": "^8.1"
},
"require-dev": {
"mockery/mockery": "^1.2",
"phpunit/phpunit": "^9.0"
},
"type": "library",
"extra": {
"laravel": {
"providers": [
"SocialiteProviders\\Manager\\ServiceProvider"
]
}
},
"autoload": {
"psr-4": {
"SocialiteProviders\\Manager\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Andy Wendt",
"email": "andy@awendt.com"
},
{
"name": "Anton Komarev",
"email": "a.komarev@cybercog.su"
},
{
"name": "Miguel Piedrafita",
"email": "soy@miguelpiedrafita.com"
},
{
"name": "atymic",
"email": "atymicq@gmail.com",
"homepage": "https://atymic.dev"
}
],
"description": "Easily add new or override built-in providers in Laravel Socialite.",
"homepage": "https://socialiteproviders.com",
"keywords": [
"laravel",
"manager",
"oauth",
"providers",
"socialite"
],
"support": {
"issues": "https://github.com/socialiteproviders/manager/issues",
"source": "https://github.com/socialiteproviders/manager"
},
"time": "2025-02-24T19:33:30+00:00"
},
{
"name": "socialiteproviders/microsoft",
"version": "4.7.0",
"source": {
"type": "git",
"url": "https://github.com/SocialiteProviders/Microsoft.git",
"reference": "824ef97a4f6e3f363c21702b76676d54e8265573"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/SocialiteProviders/Microsoft/zipball/824ef97a4f6e3f363c21702b76676d54e8265573",
"reference": "824ef97a4f6e3f363c21702b76676d54e8265573",
"shasum": ""
},
"require": {
"ext-json": "*",
"firebase/php-jwt": "^6.8",
"php": "^8.0",
"socialiteproviders/manager": "^4.4"
},
"type": "library",
"autoload": {
"psr-4": {
"SocialiteProviders\\Microsoft\\": ""
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Brian Faust",
"email": "hello@brianfaust.de"
}
],
"description": "Microsoft OAuth2 Provider for Laravel Socialite",
"keywords": [
"laravel",
"microsoft",
"oauth",
"provider",
"socialite"
],
"support": {
"docs": "https://socialiteproviders.com/microsoft",
"issues": "https://github.com/socialiteproviders/providers/issues",
"source": "https://github.com/socialiteproviders/providers"
},
"time": "2025-07-06T00:25:25+00:00"
},
{ {
"name": "spatie/laravel-package-tools", "name": "spatie/laravel-package-tools",
"version": "1.16.4", "version": "1.16.4",
+1
View File
@@ -228,6 +228,7 @@ return [
Proengsoft\JsValidation\JsValidationServiceProvider::class, Proengsoft\JsValidation\JsValidationServiceProvider::class,
Anhskohbo\NoCaptcha\NoCaptchaServiceProvider::class, Anhskohbo\NoCaptcha\NoCaptchaServiceProvider::class,
Laravel\Socialite\SocialiteServiceProvider::class, Laravel\Socialite\SocialiteServiceProvider::class,
\SocialiteProviders\Manager\ServiceProvider::class,
Webpatser\Countries\CountriesServiceProvider::class, Webpatser\Countries\CountriesServiceProvider::class,
Intervention\Image\ImageServiceProvider::class, Intervention\Image\ImageServiceProvider::class,
Jenssegers\Agent\AgentServiceProvider::class, Jenssegers\Agent\AgentServiceProvider::class,
+3 -3
View File
@@ -11,9 +11,9 @@ return [
| |
*/ */
// 'social' => [ 'social' => [
// 'providers' => ['facebook', 'twitter', 'google'], 'providers' => ['microsoft'],
// ], ],
/* /*
|-------------------------------------------------------------------------- |--------------------------------------------------------------------------
+6
View File
@@ -64,6 +64,12 @@ return [
'redirect' => env('GOOGLE_CALLBACK_URI'), 'redirect' => env('GOOGLE_CALLBACK_URI'),
], ],
'microsoft' => [
'client_id' => env('MICROSOFT_CLIENT_ID'),
'client_secret' => env('MICROSOFT_CLIENT_SECRET'),
'redirect' => env('MICROSOFT_REDIRECT_URI'),
],
// 'authy' => [ // 'authy' => [
// 'key' => env('AUTHY_KEY'), // 'key' => env('AUTHY_KEY'),
// ], // ],
File diff suppressed because it is too large Load Diff
+6
View File
@@ -0,0 +1,6 @@
# DB LOCALE (Windows 11)
url=jdbc:mysql://localhost:3306/trfcertest
username=solocla
password=!Massarosa2
driver=com.mysql.cj.jdbc.Driver
changeLogFile=liquibase/changelog/db.changelog-master.yaml
+1
View File
@@ -32,3 +32,4 @@ $langdatatables = [
"paginate_next" => "Next", "paginate_next" => "Next",
"paginate_previous" => "Previous" "paginate_previous" => "Previous"
]; ];
$quotationstitle = "Quotations";
Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Before

Width:  |  Height:  |  Size: 5.9 KiB

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 515 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 509 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 508 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 516 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 512 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 518 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 451 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 510 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 462 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 457 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 454 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

+91
View File
@@ -0,0 +1,91 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
error_reporting(E_ALL);
require_once(__DIR__ . '/include/headscript.php'); // Auth + $iduserlogin + DBHandlerSelect
try {
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
throw new Exception('Invalid request method');
}
$iddatadb = isset($_POST['iddatadb']) ? (int)$_POST['iddatadb'] : 0;
if ($iddatadb <= 0) {
throw new Exception('Missing iddatadb');
}
// ✅ Supporta sia singola stringa (con |) che array part_descriptions[]
$parts = [];
if (isset($_POST['part_descriptions']) && is_array($_POST['part_descriptions'])) {
$parts = $_POST['part_descriptions'];
} else {
$raw = isset($_POST['part_description']) ? (string)$_POST['part_description'] : '';
// split su "|" per multi-part
$parts = preg_split('/\s*\|\s*/', $raw);
}
// normalizza: trim + rimuovi vuoti + dedup
$cleanParts = [];
foreach ($parts as $p) {
$p = trim((string)$p);
if ($p === '') continue;
// limita lunghezza come da DB varchar(255)
if (mb_strlen($p) > 255) {
$p = mb_substr($p, 0, 255);
}
$cleanParts[] = $p;
}
$cleanParts = array_values(array_unique($cleanParts));
if (count($cleanParts) === 0) {
throw new Exception('Part description is empty');
}
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$pdo->beginTransaction();
try {
// prende il prossimo part_number per iddatadb
$stmtMax = $pdo->prepare("SELECT COALESCE(MAX(part_number), 0) AS maxnum FROM identification_parts WHERE iddatadb = ?");
$stmtMax->execute([$iddatadb]);
$nextNumber = (int)$stmtMax->fetchColumn() + 1;
$stmtIns = $pdo->prepare("
INSERT INTO identification_parts (iddatadb, part_number, part_description)
VALUES (?, ?, ?)
");
$insertedIds = [];
foreach ($cleanParts as $p) {
$ok = $stmtIns->execute([$iddatadb, $nextNumber, $p]);
if (!$ok) {
throw new Exception('Insert failed');
}
$insertedIds[] = $pdo->lastInsertId();
$nextNumber++;
}
$pdo->commit();
echo json_encode([
'success' => true,
'message' => 'Parts added',
'count' => count($insertedIds),
'ids' => $insertedIds
]);
} catch (Exception $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
throw $e;
}
} catch (Exception $e) {
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
exit;
+73
View File
@@ -0,0 +1,73 @@
<?php
include('include/headscript.php');
header('Content-Type: application/json');
try {
$input = json_decode(file_get_contents('php://input'), true);
$templateId = intval($input['template_id'] ?? 0);
$importRefFromClient = trim($input['importreferencecode'] ?? '');
if (!$templateId) {
throw new Exception('Template ID missing');
}
$userId = $iduserlogin ?? 1;
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
// Get default idclient from template
$stmt = $pdo->prepare("SELECT idclient FROM excel_templates WHERE id = ?");
$stmt->execute([$templateId]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
$idclient = $template['idclient'] ?? null;
// Use provided importreferencecode (from filtered page) or generate new
$importReferenceCode = $importRefFromClient !== '' ? $importRefFromClient : date('YmdHis') . '-' . uniqid();
// Insert empty record
$stmt = $pdo->prepare("
INSERT INTO datadb (templateid, user_id, status, idclient, importreferencecode, importdate)
VALUES (?, ?, 'i', ?, ?, NOW())
");
$stmt->execute([$templateId, $userId, $idclient, $importReferenceCode]);
$iddatadb = (int)$pdo->lastInsertId();
// Create import_data_details for all mappings (with auto_value support)
$mappingStmt = $pdo->prepare("SELECT id, auto_value, manual_default, data_type FROM template_mapping WHERE template_id = ?");
$mappingStmt->execute([$templateId]);
$mappings = $mappingStmt->fetchAll(PDO::FETCH_ASSOC);
if (!empty($mappings)) {
$insertStmt = $pdo->prepare("INSERT INTO import_data_details (id, mapping_id, field_value) VALUES (?, ?, ?)");
foreach ($mappings as $m) {
$val = '';
$auto = $m['auto_value'] ?? 'none';
if ($auto === 'import_date') {
$val = date('Y-m-d');
} elseif ($auto === 'import_time') {
$val = date('H:i');
} elseif ($m['data_type'] === 'DATE' && ($m['manual_default'] ?? '') === 'today') {
$val = date('Y-m-d');
} elseif (!empty($m['manual_default'])) {
$val = $m['manual_default'];
}
$insertStmt->execute([$iddatadb, $m['id'], $val]);
}
}
// Get user name
$userStmt = $pdo->prepare("SELECT CONCAT(first_name, ' ', last_name) AS user_name FROM auth_users WHERE id = ?");
$userStmt->execute([$userId]);
$userName = $userStmt->fetchColumn() ?: '';
echo json_encode([
'success' => true,
'iddatadb' => $iddatadb,
'importreferencecode' => $importReferenceCode,
'user_name' => $userName,
]);
} catch (Exception $e) {
http_response_code(500);
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
+854
View File
@@ -0,0 +1,854 @@
(function () {
"use strict";
let analysisMatriciMap = {};
let analysisLoadedCache = {};
let analysisAssignedState = {};
let analysisSelectedState = {};
function loadAnalysisMatrixNames() {
return $.ajax({
url: "get_matrici_db.php",
method: "GET",
dataType: "json",
})
.done(function (data) {
analysisMatriciMap = {};
(data.value || []).forEach(function (matrice) {
analysisMatriciMap[String(matrice.IdMatrice)] =
matrice.NomeMatrice || "#" + matrice.IdMatrice;
});
applyAnalysisMatrixNames();
})
.fail(function () {
analysisMatriciMap = {};
applyAnalysisMatrixNames();
});
}
function applyAnalysisMatrixNames() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
const matrixId = item.getAttribute("data-matrix-id");
const nameEl = item.querySelector(".analysis-matrix-name");
let matrixName = "No Matrix";
if (matrixId && matrixId !== "NO_MATRIX") {
matrixName =
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
}
item.setAttribute("data-matrix-name", matrixName);
if (nameEl) {
nameEl.textContent = matrixName;
}
});
modal.querySelectorAll(".analysis-part-matrix-name").forEach((el) => {
const matrixId = el.getAttribute("data-matrix-id");
if (!matrixId || matrixId === "NO_MATRIX") {
el.textContent = "No Matrix";
return;
}
el.textContent =
analysisMatriciMap[String(matrixId)] || "#" + matrixId;
});
}
function readInitialAssignedAnalyses() {
const jsonEl = document.getElementById("analysisAssignedInitialData");
if (!jsonEl) {
analysisAssignedState = {};
return;
}
try {
analysisAssignedState =
JSON.parse(jsonEl.textContent || "{}") || {};
} catch (e) {
analysisAssignedState = {};
}
}
function getSelectedPartIds() {
const modal = document.getElementById("analysisModal");
if (!modal) return [];
return Array.from(
modal.querySelectorAll(".analysis-part-checkbox:checked"),
).map((el) => String(el.value));
}
function getCurrentSelectedAnalysisRecordKeys() {
return Object.keys(analysisSelectedState).filter(function (key) {
return analysisSelectedState[key] === true;
});
}
function renderAssignedAnalysesForPart(partId) {
const container = document.getElementById(
"analysisAssignedListPart" + partId,
);
if (!container) return;
const items = Array.isArray(analysisAssignedState[String(partId)])
? analysisAssignedState[String(partId)]
: [];
if (!items.length) {
container.innerHTML = "";
return;
}
container.innerHTML = items
.map(function (item) {
const recordKey = item.analysis_recordkey || "";
const title = item.analysis_name || "Unnamed analysis";
const method = item.analysis_method || "";
return `
<div class="analysis-assigned-chip" data-recordkey="${escapeHtml(recordKey)}">
<div class="analysis-assigned-chip-text">
<div class="analysis-assigned-chip-title">${escapeHtml(title)}</div>
${method ? `<div class="analysis-assigned-chip-method">${escapeHtml(method)}</div>` : ""}
</div>
<button type="button"
class="analysis-remove-btn"
data-part-id="${escapeHtml(partId)}"
data-recordkey="${escapeHtml(recordKey)}"
title="Remove analysis">×</button>
</div>
`;
})
.join("");
}
function renderAssignedAnalysesForSelectedParts() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
modal.querySelectorAll(".analysis-part-item").forEach(function (item) {
const partId = item.getAttribute("data-part-id");
renderAssignedAnalysesForPart(partId);
});
}
function syncSelectedAnalysisRows() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
modal
.querySelectorAll(".analysis-analysis-item")
.forEach(function (item) {
const recordKey = item.getAttribute("data-recordkey") || "";
if (recordKey && analysisSelectedState[recordKey]) {
item.classList.add("analysis-selected");
} else {
item.classList.remove("analysis-selected");
}
});
}
function buildAnalysisPayloadFromRow(rowEl) {
return {
analysis_recordkey: rowEl.getAttribute("data-recordkey") || "",
analysis_name: rowEl.getAttribute("data-analysis-name") || "",
analysis_method: rowEl.getAttribute("data-analysis-method") || "",
analysis_level: rowEl.getAttribute("data-analysis-level") || "",
is_web_selectable: rowEl.getAttribute("data-web") === "1" ? 1 : 0,
is_accredited:
rowEl.getAttribute("data-accredited") === "1" ? 1 : 0,
};
}
function addAnalysisToLocalState(partId, payload, iddatadb, idmatrice) {
const key = String(partId);
if (!Array.isArray(analysisAssignedState[key])) {
analysisAssignedState[key] = [];
}
const exists = analysisAssignedState[key].some(function (item) {
return item.analysis_recordkey === payload.analysis_recordkey;
});
if (!exists) {
analysisAssignedState[key].push({
id: null,
part_id: parseInt(partId, 10),
iddatadb: iddatadb || null,
idmatrice: idmatrice || null,
analysis_recordkey: payload.analysis_recordkey,
analysis_name: payload.analysis_name,
analysis_method: payload.analysis_method,
analysis_level:
payload.analysis_level !== ""
? parseInt(payload.analysis_level, 10)
: null,
is_web_selectable: payload.is_web_selectable,
is_accredited: payload.is_accredited,
});
}
}
function removeAnalysisFromLocalState(partId, recordKey) {
const key = String(partId);
if (!Array.isArray(analysisAssignedState[key])) {
return;
}
analysisAssignedState[key] = analysisAssignedState[key].filter(
function (item) {
return item.analysis_recordkey !== recordKey;
},
);
}
function saveAnalysisAssociation(partId, payload, callback) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const iddatadb =
modal.querySelector("#analysisModalIddatadb")?.value || "";
const partItem = modal.querySelector(
'.analysis-part-item[data-part-id="' + partId + '"]',
);
const idmatrice = partItem
? partItem.getAttribute("data-matrix-id") || ""
: "";
$.ajax({
url: "save_part_analysis.php",
method: "POST",
dataType: "json",
data: {
part_id: partId,
iddatadb: iddatadb,
idmatrice: idmatrice !== "NO_MATRIX" ? idmatrice : "",
analysis_recordkey: payload.analysis_recordkey,
analysis_name: payload.analysis_name,
analysis_method: payload.analysis_method,
analysis_level: payload.analysis_level,
is_web_selectable: payload.is_web_selectable,
is_accredited: payload.is_accredited,
},
})
.done(function () {
addAnalysisToLocalState(
partId,
payload,
iddatadb,
idmatrice !== "NO_MATRIX" ? idmatrice : null,
);
renderAssignedAnalysesForPart(partId);
if (typeof callback === "function") callback(true);
})
.fail(function (xhr) {
let message = "Error saving analysis association";
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
alert(message);
if (typeof callback === "function") callback(false);
});
}
function deleteAnalysisAssociation(partId, recordKey, callback) {
$.ajax({
url: "delete_part_analysis.php",
method: "POST",
dataType: "json",
data: {
part_id: partId,
analysis_recordkey: recordKey,
},
})
.done(function () {
removeAnalysisFromLocalState(partId, recordKey);
renderAssignedAnalysesForPart(partId);
if (typeof callback === "function") callback(true);
})
.fail(function (xhr) {
let message = "Error deleting analysis association";
if (xhr && xhr.responseJSON && xhr.responseJSON.message) {
message = xhr.responseJSON.message;
}
alert(message);
if (typeof callback === "function") callback(false);
});
}
function setAnalysisLoadingState(isLoading) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const loadingEl = modal.querySelector("#analysisLoadingBox");
if (!loadingEl) return;
if (isLoading) {
loadingEl.classList.remove("d-none");
} else {
loadingEl.classList.add("d-none");
}
}
function showAnalysisError(message) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const errorEl = modal.querySelector("#analysisErrorBox");
const emptyEl = modal.querySelector("#analysisEmptyBox");
const listEl = modal.querySelector("#analysisList");
if (listEl) listEl.innerHTML = "";
if (emptyEl) emptyEl.classList.add("d-none");
if (errorEl) {
errorEl.textContent = message || "Error loading analyses";
errorEl.classList.remove("d-none");
}
}
function showAnalysisEmpty(message) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const errorEl = modal.querySelector("#analysisErrorBox");
const emptyEl = modal.querySelector("#analysisEmptyBox");
const listEl = modal.querySelector("#analysisList");
if (listEl) listEl.innerHTML = "";
if (errorEl) errorEl.classList.add("d-none");
if (emptyEl) {
emptyEl.textContent =
message || "No analyses found for this matrix";
emptyEl.classList.remove("d-none");
}
}
function escapeHtml(value) {
return String(value ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#039;");
}
function renderAnalysesList(analyses) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const errorEl = modal.querySelector("#analysisErrorBox");
const emptyEl = modal.querySelector("#analysisEmptyBox");
const listEl = modal.querySelector("#analysisList");
if (!listEl) return;
if (errorEl) errorEl.classList.add("d-none");
if (!Array.isArray(analyses) || analyses.length === 0) {
showAnalysisEmpty("No analyses found for this matrix");
return;
}
if (emptyEl) emptyEl.classList.add("d-none");
listEl.innerHTML = analyses
.map(function (item) {
const recordKey = item.RecordKey || "";
const analysisName =
item.NomeAnalisiTraduzione ||
item.NomeAnalisi ||
"Unnamed analysis";
const methodName = item.MetodoNome || "";
const selectable = item.SelezionabileSuWeb === true;
const accredited = item.Accreditato === true;
const level = item.Livello ?? "";
const searchText = (
analysisName +
" " +
methodName
).toLowerCase();
let badges = "";
if (selectable) {
badges += '<span class="badge bg-success">Web</span>';
} else {
badges += '<span class="badge bg-secondary">Not web</span>';
}
if (accredited) {
badges +=
'<span class="badge bg-info text-dark">Accredited</span>';
}
if (level !== "") {
badges +=
'<span class="badge bg-light text-dark border">Level ' +
escapeHtml(level) +
"</span>";
}
return `
<div
class="list-group-item analysis-analysis-item"
data-recordkey="${escapeHtml(recordKey)}"
data-analysis-name="${escapeHtml(analysisName)}"
data-analysis-method="${escapeHtml(methodName)}"
data-analysis-level="${escapeHtml(level)}"
data-web="${selectable ? "1" : "0"}"
data-accredited="${accredited ? "1" : "0"}"
data-search="${escapeHtml(searchText)}"
>
<div class="fw-semibold">${escapeHtml(analysisName)}</div>
${methodName ? `<div class="analysis-analysis-meta mt-1">${escapeHtml(methodName)}</div>` : ""}
<div class="analysis-analysis-badges mt-2">
${badges}
</div>
</div>
`;
})
.join("");
filterAnalysisList();
syncSelectedAnalysisRows();
}
function filterAnalysisList() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const webOnlyEl = modal.querySelector("#analysisWebOnly");
const searchEl = modal.querySelector("#analysisSearchInput");
const items = modal.querySelectorAll(".analysis-analysis-item");
const emptyEl = modal.querySelector("#analysisEmptyBox");
const errorEl = modal.querySelector("#analysisErrorBox");
const webOnly = true;
const searchValue = searchEl ? searchEl.value.trim().toLowerCase() : "";
let visibleCount = 0;
items.forEach((item) => {
const isWeb = item.getAttribute("data-web") === "1";
const searchText = (
item.getAttribute("data-search") || ""
).toLowerCase();
let visible = true;
if (webOnly && !isWeb) {
visible = false;
}
if (visible && searchValue && !searchText.includes(searchValue)) {
visible = false;
}
item.style.display = visible ? "" : "none";
if (visible) {
visibleCount++;
}
});
if (errorEl) {
errorEl.classList.add("d-none");
}
if (emptyEl) {
if (items.length === 0) {
emptyEl.textContent = "No analyses found for this matrix";
emptyEl.classList.remove("d-none");
} else if (visibleCount === 0) {
emptyEl.textContent = "No analyses match the current filters";
emptyEl.classList.remove("d-none");
} else {
emptyEl.classList.add("d-none");
}
}
}
function loadAnalysesByMatrix(matrixId) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
if (!matrixId || matrixId === "NO_MATRIX") {
showAnalysisEmpty("No matrix selected");
return;
}
const listEl = modal.querySelector("#analysisList");
const errorEl = modal.querySelector("#analysisErrorBox");
const emptyEl = modal.querySelector("#analysisEmptyBox");
if (listEl) listEl.innerHTML = "";
if (errorEl) errorEl.classList.add("d-none");
if (emptyEl) {
emptyEl.textContent = "";
emptyEl.classList.add("d-none");
}
const cacheKey = String(matrixId) + "_WEB_ONLY";
if (analysisLoadedCache[cacheKey]) {
renderAnalysesList(analysisLoadedCache[cacheKey]);
return;
}
setAnalysisLoadingState(true);
$.ajax({
url: "get_analisi_matrice_filter.php",
method: "GET",
dataType: "json",
data: {
id_matrice: matrixId,
web_only: 1,
},
})
.done(function (response) {
const analyses = Array.isArray(response.value)
? response.value.filter(function (item) {
return (
item.SelezionabileSuWeb === true ||
item.SelezionabileSuWeb === 1 ||
item.SelezionabileSuWeb === "1"
);
})
: [];
analysisLoadedCache[cacheKey] = analyses;
renderAnalysesList(analyses);
})
.fail(function (xhr) {
let message = "Error loading analyses";
if (xhr && xhr.responseJSON && xhr.responseJSON.error) {
message = xhr.responseJSON.error;
}
showAnalysisError(message);
})
.always(function () {
setAnalysisLoadingState(false);
});
}
function updateSelectedPartsInfo() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const checked = modal.querySelectorAll(
".analysis-part-checkbox:checked",
);
const ids = Array.from(checked).map((el) => el.value);
const countEl = modal.querySelector("#analysisSelectedPartsCount");
const idsEl = modal.querySelector("#analysisSelectedPartsIds");
if (countEl) {
countEl.textContent = `${ids.length} selected`;
}
if (idsEl) {
idsEl.textContent = ids.length ? ids.join(", ") : "-";
}
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
const checkbox = item.querySelector(".analysis-part-checkbox");
if (checkbox && checkbox.checked) {
item.classList.add("part-checked");
} else {
item.classList.remove("part-checked");
}
});
}
function selectPartsByMatrix(matrixId, matrixLabel) {
const modal = document.getElementById("analysisModal");
if (!modal) return;
const partItems = modal.querySelectorAll(".analysis-part-item");
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
partItems.forEach((item) => {
const itemMatrixId = item.getAttribute("data-matrix-id");
const checkbox = item.querySelector(".analysis-part-checkbox");
item.classList.remove("matrix-active");
if (itemMatrixId === String(matrixId)) {
item.classList.add("matrix-active");
if (checkbox) checkbox.checked = true;
} else {
if (checkbox) checkbox.checked = false;
}
});
if (matrixLabelEl) {
matrixLabelEl.textContent = matrixLabel || "-";
}
analysisSelectedState = {};
const selectedPartIds = getSelectedPartIds();
selectedPartIds.forEach(function (partId) {
const assigned = Array.isArray(
analysisAssignedState[String(partId)],
)
? analysisAssignedState[String(partId)]
: [];
assigned.forEach(function (item) {
if (item.analysis_recordkey) {
analysisSelectedState[item.analysis_recordkey] = true;
}
});
});
updateSelectedPartsInfo();
loadAnalysesByMatrix(matrixId);
}
function clearAnalysisSelection() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
modal.querySelectorAll(".analysis-matrix-item").forEach((item) => {
item.classList.remove("active");
});
modal.querySelectorAll(".analysis-part-item").forEach((item) => {
item.classList.remove("matrix-active", "part-checked");
});
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
cb.checked = false;
});
const matrixLabelEl = modal.querySelector("#analysisCurrentMatrix");
if (matrixLabelEl) matrixLabelEl.textContent = "-";
const webOnlyEl = modal.querySelector("#analysisWebOnly");
if (webOnlyEl) webOnlyEl.checked = false;
const searchEl = modal.querySelector("#analysisSearchInput");
if (searchEl) searchEl.value = "";
const listEl = modal.querySelector("#analysisList");
if (listEl) listEl.innerHTML = "";
showAnalysisEmpty("Select a matrix to load analyses");
updateSelectedPartsInfo();
}
function initAnalysisModal() {
const modal = document.getElementById("analysisModal");
if (!modal) return;
analysisLoadedCache = {};
analysisSelectedState = {};
readInitialAssignedAnalyses();
renderAssignedAnalysesForSelectedParts();
modal.querySelectorAll(".analysis-matrix-item").forEach((btn) => {
btn.addEventListener("click", function () {
modal
.querySelectorAll(".analysis-matrix-item")
.forEach((x) => x.classList.remove("active"));
this.classList.add("active");
const matrixId = this.getAttribute("data-matrix-id");
const matrixLabel =
this.getAttribute("data-matrix-name") ||
this.textContent.trim();
selectPartsByMatrix(matrixId, matrixLabel);
});
});
modal.querySelectorAll(".analysis-part-checkbox").forEach((cb) => {
cb.addEventListener("change", function () {
updateSelectedPartsInfo();
});
});
const clearBtn = modal.querySelector("#analysisClearSelectionBtn");
if (clearBtn) {
clearBtn.addEventListener("click", function () {
clearAnalysisSelection();
});
}
// WEB only is now fixed by default
const searchEl = modal.querySelector("#analysisSearchInput");
if (searchEl) {
searchEl.addEventListener("input", function () {
filterAnalysisList();
});
}
modal.addEventListener("click", function (e) {
const removeBtn = e.target.closest(".analysis-remove-btn");
if (removeBtn) {
e.preventDefault();
e.stopPropagation();
const partId = removeBtn.getAttribute("data-part-id");
const recordKey = removeBtn.getAttribute("data-recordkey");
deleteAnalysisAssociation(partId, recordKey, function () {
const stillUsed = Object.keys(analysisAssignedState).some(
function (pid) {
return (
Array.isArray(analysisAssignedState[pid]) &&
analysisAssignedState[pid].some(
function (item) {
return (
item.analysis_recordkey ===
recordKey
);
},
)
);
},
);
if (!stillUsed) {
delete analysisSelectedState[recordKey];
}
syncSelectedAnalysisRows();
});
return;
}
const row = e.target.closest(".analysis-analysis-item");
if (!row) return;
const selectedPartIds = getSelectedPartIds();
if (!selectedPartIds.length) {
alert("Select at least one part first");
return;
}
const payload = buildAnalysisPayloadFromRow(row);
if (!payload.analysis_recordkey) {
alert("Invalid analysis record key");
return;
}
const recordKey = payload.analysis_recordkey;
const alreadySelected = !!analysisSelectedState[recordKey];
if (alreadySelected) {
selectedPartIds.forEach(function (partId) {
deleteAnalysisAssociation(partId, recordKey);
});
delete analysisSelectedState[recordKey];
syncSelectedAnalysisRows();
return;
}
let pending = selectedPartIds.length;
let atLeastOneSaved = false;
selectedPartIds.forEach(function (partId) {
saveAnalysisAssociation(partId, payload, function (ok) {
if (ok) atLeastOneSaved = true;
pending--;
if (pending <= 0 && atLeastOneSaved) {
analysisSelectedState[recordKey] = true;
syncSelectedAnalysisRows();
}
});
});
});
const firstActiveMatrix = modal.querySelector(
".analysis-matrix-item.active",
);
if (firstActiveMatrix) {
const matrixId = firstActiveMatrix.getAttribute("data-matrix-id");
const matrixLabel =
firstActiveMatrix.getAttribute("data-matrix-name") ||
firstActiveMatrix.textContent.trim();
selectPartsByMatrix(matrixId, matrixLabel);
} else {
updateSelectedPartsInfo();
}
}
// OPEN ANALYSIS MODAL FROM PARTS MODAL BUTTON
$(document).on("click", ".open-analysis-modal-btn", function () {
const partsModal = document.getElementById("partsModal");
const iddatadb = $("#partsModal").data("iddatadb");
if (!iddatadb) {
console.error("iddatadb not found on #partsModal");
alert("iddatadb not found");
return;
}
$.ajax({
url: "modal_analysis.php",
method: "GET",
data: { iddatadb: iddatadb },
success: function (response) {
$("#analysisModalContainer").html(response);
const modalElement = document.getElementById("analysisModal");
if (!modalElement) {
console.error("Analysis modal not found: #analysisModal");
return;
}
let modal = bootstrap.Modal.getInstance(modalElement);
if (!modal) {
modal = new bootstrap.Modal(modalElement, {
backdrop: true,
keyboard: true,
focus: true,
});
}
loadAnalysisMatrixNames().always(function () {
initAnalysisModal();
modal.show();
});
},
error: function (xhr, status, error) {
console.error("Error loading analysis modal:", error);
alert("Error loading analysis modal: " + error);
},
});
});
// CLEANUP ON CLOSE
$(document).on("hidden.bs.modal", "#analysisModal", function () {
const modalElement = document.getElementById("analysisModal");
if (modalElement) {
const modal = bootstrap.Modal.getInstance(modalElement);
if (modal) modal.dispose();
}
$("#analysisModalContainer").empty();
// keep parts modal alive, but remove extra backdrop leftovers
$(".modal-backdrop").last().remove();
if ($("#partsModal").hasClass("show")) {
$("body").addClass("modal-open");
} else {
$("body").removeClass("modal-open").css("padding-right", "");
}
});
})();
File diff suppressed because it is too large Load Diff
+53
View File
@@ -0,0 +1,53 @@
<?php
ob_start();
session_start();
require_once '../../vendor/autoload.php';
Dotenv\Dotenv::createImmutable(dirname(__DIR__, 2))->safeLoad();
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
$response = ['error' => '', 'rows' => [], 'columns' => [], 'template_id' => 0, 'filename' => '', 'excel_data' => []];
try {
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$input = json_decode(file_get_contents('php://input'), true);
$template_id = isset($input['template_id']) ? intval($input['template_id']) : 0;
$filename = $input['routine_data']['filename'] ?? '';
$headerrow = $input['routine_data']['headerrow'] ?? 1;
$excelData = $input['excel_data'] ?? [];
$routineData = $input['routine_data'] ?? [];
if (!$filename || empty($excelData)) {
throw new Exception("Dati della routine mancanti.");
}
$routineFile = __DIR__ . '/routines/' . $filename;
if (file_exists($routineFile)) {
include_once $routineFile;
$routineData['xls_headers'] = $_SESSION['headers'] ?? [];
applyRoutine($excelData, $routineData); // Modifica $excelData in place
error_log("Routine {$routineData['name']} applicata (file: {$filename}) per template {$template_id}, header row: {$headerrow}");
} else {
throw new Exception("File della routine non trovato: $routineFile");
}
// Aggiorna la sessione con i dati modificati
$_SESSION['excel_data'] = $excelData;
$response['excel_data'] = $excelData;
$response['rows'] = array_column($excelData, 'data');
$response['columns'] = $_SESSION['headers'];
$response['template_id'] = $template_id;
$response['filename'] = $input['filename'] ?? '';
} else {
$response['error'] = "Richiesta non valida.";
}
} catch (Exception $e) {
$response['error'] = "Errore durante l'applicazione della routine: " . $e->getMessage();
error_log("Exception in apply_routine.php: " . $e->getMessage());
}
ob_end_clean();
header('Content-Type: application/json');
echo json_encode($response);
exit;
+14 -39
View File
@@ -17,63 +17,38 @@ curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($credentials)); curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($credentials));
curl_setopt($ch, CURLOPT_HTTPHEADER, [ curl_setopt($ch, CURLOPT_HTTPHEADER, [
'Content-Type: application/json', 'Content-Type: application/json',
'Accept: application/json', 'Accept: application/json'
'User-Agent: Mozilla/5.0 (compatible; PHP cURL)'
]); ]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Disabilita verifica SSL (solo test) curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); // Solo per test
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Disabilita verifica host (solo test) curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); // Solo per test
curl_setopt($ch, CURLOPT_VERBOSE, true); // Abilita debug curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen('curl_debug.log', 'a'); // Usa 'a' per appendere al log $log = fopen('curl_auth_debug.log', 'w');
// Gestione degli errori di apertura del file di log
if ($log === false) {
http_response_code(500);
echo json_encode([
'error' => 'Impossibile aprire il file di log per il debug'
]);
exit;
}
curl_setopt($ch, CURLOPT_STDERR, $log); curl_setopt($ch, CURLOPT_STDERR, $log);
// Esegui la richiesta // Esegui la richiesta
$response = curl_exec($ch); $response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch); $curl_error = curl_error($ch);
fclose($log); fclose($log);
// Log della risposta per debug // Verifica errori
file_put_contents('curl_response.log', "HTTP Code: $http_code\nResponse: $response\n\n", FILE_APPEND); if ($response === false || $http_code != 200) {
// Verifica errori di esecuzione cURL
if ($response === false) {
http_response_code($http_code ? $http_code : 500); http_response_code($http_code ? $http_code : 500);
echo json_encode([ echo json_encode([
'error' => 'Errore nella richiesta API', 'error' => 'Errore nella richiesta API',
'http_code' => $http_code, 'http_code' => $http_code,
'curl_error' => $curl_error 'curl_error' => $curl_error,
'response' => substr($response, 0, 1000)
]); ]);
curl_close($ch);
exit;
}
// Rimuovi eventuali virgolette e spazi dalla risposta
$trimmed_response = trim($response, '" \t\n\r\0\x0B');
// Se la risposta è una stringa non vuota, considerala il token
if (is_string($trimmed_response) && !empty($trimmed_response)) {
http_response_code($http_code);
echo json_encode(['token' => $trimmed_response]);
} else { } else {
// Tenta di decodificare la risposta come JSON $decoded = json_decode($response);
$decoded = json_decode($response, true); if (json_last_error() === JSON_ERROR_NONE) {
if (json_last_error() === JSON_ERROR_NONE && isset($decoded['token']) && is_string($decoded['token']) && !empty($decoded['token'])) {
http_response_code($http_code); http_response_code($http_code);
echo json_encode(['token' => $decoded['token']]); echo $response;
} else { } else {
http_response_code($http_code); http_response_code(500);
echo json_encode([ echo json_encode([
'error' => 'Risposta non valida o token non trovato', 'error' => 'Risposta non JSON valida',
'http_code' => $http_code, 'http_code' => $http_code,
'response' => substr($response, 0, 1000) 'response' => substr($response, 0, 1000)
]); ]);
+131
View File
@@ -0,0 +1,131 @@
<?php
require_once "class/VisualLimsApiClient.class.php";
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
header("Content-Type: application/json");
// 🔹 Configura directory log (creala se non esiste)
$logDir = __DIR__ . '/logs/api/';
if (!is_dir($logDir)) {
mkdir($logDir, 0755, true);
}
// 🔹 Base URL API
$apiBaseUrl = 'https://93.43.5.102/limsapi/api/odata/';
// 🔹 Hardcoded values
$iddatadb = 846;
$commessaId = 533357;
try {
// 🔹 STEP 4: Fetch Field Values with Labels (usa dati reali per iddatadb=845)
$stmt = $pdo->prepare("
SELECT
idd.field_value,
m.field_label,
m.schema_id,
m.field_id
FROM
import_data_details as idd
JOIN template_mapping m ON idd.mapping_id = m.id
WHERE idd.id = :iddatadb
");
$stmt->execute(['iddatadb' => $iddatadb]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
$fieldValues = [];
$valueMap = []; // Mappa per field_id -> valore
foreach ($rows as $row) {
$fieldValues[] = [
"IdCommesseCustomFields" => (int) $row['field_id'],
"Valore" => $row['field_value'],
"FieldLabel" => $row['field_label']
];
$valueMap[(int) $row['field_id']] = $row['field_value']; // Mappa per ID definizione
}
// Logga i fieldValues in error_log
$logFieldValues = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT);
error_log($logFieldValues);
// 🔹 Initialize API client
$api = VisualLimsApiClient::getInstance();
// 🔹 STEP A: GET iniziale per CommesseCustomFields con espansione CustomField
$expand = "CommesseCustomFields(\$expand=CustomField)"; // Espansione come da istruzioni fornitore
$commessaWithFields = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
// 🔹 STEP B: Prepara payload PATCH (tutti i campi, sovrascrivi se match su CustomField.IdCustomField == field_id)
$commessaCustomFields = [];
foreach ($commessaWithFields["CommesseCustomFields"] as $customField) {
$definitionId = (int) ($customField["CustomField"]["IdCustomField"] ?? 0); // Usa IdCustomField dal CustomField
$currentValue = $customField["Valore"] ?? '';
$newValue = isset($valueMap[$definitionId]) ? $valueMap[$definitionId] : $currentValue;
$commessaCustomFields[] = [
"IdCommesseCustomFields" => (int) $customField["IdCommesseCustomFields"],
"Valore" => $newValue
];
}
// 🔹 Unico file di log per tutto
$logFile = $logDir . "commessa_{$commessaId}_patch_and_get_" . time() . ".txt";
$logContent = "FieldValues dal DB (iddatadb={$iddatadb}):\n" . json_encode($fieldValues, JSON_PRETTY_PRINT) . "\n\n";
// Log curl-like per GET iniziale
$logContent .= "GET iniziale:\n" .
"curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
"--header 'Authorization: Bearer ••••••'\n\n" .
"RESPONSE:\n" . json_encode($commessaWithFields, JSON_PRETTY_PRINT) . "\n\n---\n";
if (!empty($commessaCustomFields)) {
$updatePayload = ["CommesseCustomFields" => $commessaCustomFields];
// Log curl-like per PATCH
$jsonPayload = json_encode($updatePayload, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$logContent .= "PATCH:\n" .
"curl --location --request PATCH '{$apiBaseUrl}CommessaWeb({$commessaId})' \\\n" .
"--header 'Content-Type: application/json' \\\n" .
"--header 'Authorization: Bearer ••••••' \\\n" .
"--data '{$jsonPayload}'\n\n";
$patchResponse = $api->patch("CommessaWeb({$commessaId})", $updatePayload);
$logContent .= "PATCH RESPONSE:\n" . json_encode($patchResponse, JSON_PRETTY_PRINT) . "\n\n---\n";
} else {
$logContent .= "PATCH: Nessun campo custom da aggiornare\n\n---\n";
}
// 🔹 STEP C: GET di controllo post-PATCH
$commessaAfterPatch = $api->get("CommessaWeb(" . $commessaId . ")?\$expand=" . $expand);
// Log curl-like per GET di controllo
$logContent .= "GET di controllo:\n" .
"curl --location --request GET '{$apiBaseUrl}CommessaWeb({$commessaId})?\$expand=CommesseCustomFields(\$expand=CustomField)' \\\n" .
"--header 'Authorization: Bearer ••••••'\n\n" .
"RESPONSE:\n" . json_encode($commessaAfterPatch, JSON_PRETTY_PRINT);
// Salva log unico
file_put_contents($logFile, $logContent);
// 🔹 Output a schermo
echo json_encode([
"success" => true,
"message" => "PATCH eseguito su commessa {$commessaId} con dati da iddatadb {$iddatadb}",
"commessaAfterPatch" => $commessaAfterPatch,
"totalCustomFieldsUpdated" => count($commessaCustomFields),
"fieldValues" => $fieldValues,
"logFile" => $logFile
]);
} catch (Exception $e) {
error_log("Patch Error: " . $e->getMessage() . "\nTrace: " . $e->getTraceAsString());
echo json_encode([
"success" => false,
"message" => "Patch failed: " . $e->getMessage(),
"logFile" => $logFile ?? 'Nessun log generato'
]);
}
@@ -0,0 +1,293 @@
<?php
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Torna al livello di public
use Dotenv\Dotenv;
class VisualLimsApiClient
{
private static $instance = null;
private $baseUrl;
private $username;
private $password;
private $token = null;
private function __construct()
{
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3)); // Torna al livello di public
$dotenv->load();
$this->baseUrl = $_ENV['API_BASE_URL'];
$this->username = $_ENV['API_USERNAME'];
$this->password = $_ENV['API_PASSWORD'];
}
public static function getInstance()
{
if (self::$instance === null) {
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
$dotenv->load();
$simulate = ($_ENV['SIMULATE_EXPORT_LIMS'] ?? '') === 'true';
if ($simulate) {
require_once __DIR__ . '/VisualLimsApiClientMock.class.php';
self::$instance = new VisualLimsApiClientMock();
} else {
self::$instance = new VisualLimsApiClient();
}
}
return self::$instance;
}
private function authenticate($retryCount = 0, $maxRetries = 3)
{
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'Username' => $this->username,
'Password' => $this->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);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'a') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
$log_message = date('Y-m-d H:i:s') . " - Auth attempt {$retryCount}: HTTP {$http_code}, Error: {$curl_error}, Response: " . substr($response, 0, 1000) . "\n";
fwrite($log, $log_message);
fclose($log);
curl_close($ch);
if ($response === false || $http_code != 200) {
if ($http_code === 400 && strpos($response, 'Cannot persist the object') !== false && $retryCount < $maxRetries) {
usleep(500000); // Ritardo di 500ms
return $this->authenticate($retryCount + 1, $maxRetries); // Riprova
}
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
}
$token_data = json_decode($response, true);
$this->token = null;
if (is_array($token_data) && isset($token_data['token'])) {
$this->token = $token_data['token'];
} elseif (is_string($token_data) && !empty($token_data)) {
$this->token = trim($token_data, '"');
} elseif (is_string($response) && !empty($response)) {
$this->token = trim($response, '"');
}
if (empty($this->token)) {
throw new Exception("Token non ricevuto: " . substr($response, 0, 1000));
}
}
private function getToken()
{
if ($this->token === null) {
$this->authenticate();
}
return $this->token;
}
public function get($endpoint)
{
$token = $this->getToken();
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Accept: application/json"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_request_debug.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta: {$curl_error}");
}
if ($http_code !== 200) {
throw new Exception("Errore nel recupero dati: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("Risposta non JSON valida: " . substr($response, 0, 1000));
}
return $data;
}
public function post($endpoint, $payload)
{
$token = $this->getToken();
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Content-Type: application/json",
"Accept: application/json"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta POST: {$curl_error}");
}
if ($http_code < 200 || $http_code >= 300) {
throw new Exception("POST fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
return json_decode($response, true);
}
public function patch($endpoint, $payload)
{
$token = $this->getToken();
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "PATCH");
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($payload));
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Content-Type: application/json",
"Accept: application/json"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta PATCH: {$curl_error}");
}
if ($http_code < 200 || $http_code >= 300) {
throw new Exception("PATCH fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
return json_decode($response, true);
}
/**
* POST a file as multipart/form-data (used for photo/attachment uploads).
*
* @param string $endpoint OData endpoint, e.g. "Campione(613388)/UploadCampioneFile"
* @param string $filePath Absolute path to the file on disk
* @param string $fileName Original file name to send
* @param array $extraFields Additional form fields to include
* @return array|null Decoded JSON response
*/
public function postMultipart($endpoint, $filePath, $fileName, array $extraFields = [])
{
$token = $this->getToken();
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
$cfile = new CURLFile($filePath, mime_content_type($filePath) ?: 'application/octet-stream', $fileName);
$payload = array_merge($extraFields, [
'file' => $cfile,
]);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Accept: application/json",
// Content-Type is set automatically to multipart/form-data by cURL
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta POST multipart: {$curl_error}");
}
if ($http_code < 200 || $http_code >= 300) {
throw new Exception("POST multipart fallito: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
return json_decode($response, true);
}
public function getBaseUrl()
{
return $this->baseUrl;
}
/**
* Recupera contenuto binario - Adattato per https://bvcpsitaly-elims.com/limsapi
*/
public function getRaw($endpoint)
{
$token = $this->getToken();
// IMPORTANTE: usa /odata/ e NON /api/odata/
$url = "{$this->baseUrl}/odata/{$endpoint}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Accept: */*"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore cURL: " . $curl_error);
}
if ($http_code !== 200) {
throw new Exception("HTTP {$http_code} su endpoint: " . $url);
}
return $response;
}
}
@@ -0,0 +1,123 @@
<?php
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Torna al livello di public
use Dotenv\Dotenv;
class VisualLimsApiClient
{
private static $instance = null;
private $baseUrl;
private $username;
private $password;
private $token = null;
private function __construct()
{
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3)); // Torna al livello di public
$dotenv->load();
$this->baseUrl = $_ENV['API_BASE_URL'];
$this->username = $_ENV['API_USERNAME'];
$this->password = $_ENV['API_PASSWORD'];
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new VisualLimsApiClient();
}
return self::$instance;
}
private function authenticate()
{
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'Username' => $this->username,
'Password' => $this->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);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_auth_debug.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false || $http_code != 200) {
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
}
$token_data = json_decode($response, true);
$this->token = null;
if (is_array($token_data) && isset($token_data['token'])) {
$this->token = $token_data['token'];
} elseif (is_string($token_data) && !empty($token_data)) {
$this->token = trim($token_data, '"');
} elseif (is_string($response) && !empty($response)) {
$this->token = trim($response, '"');
}
if (empty($this->token)) {
throw new Exception("Token non ricevuto: " . substr($response, 0, 1000));
}
}
private function getToken()
{
if ($this->token === null) {
$this->authenticate();
}
return $this->token;
}
public function get($endpoint)
{
$token = $this->getToken();
$url = "{$this->baseUrl}/api/odata/{$endpoint}";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, [
"Authorization: Bearer {$token}",
"Accept: application/json"
]);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_request_debug.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta: {$curl_error}");
}
if ($http_code !== 200) {
throw new Exception("Errore nel recupero dati: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
$data = json_decode($response, true);
if (json_last_error() !== JSON_ERROR_NONE) {
throw new Exception("Risposta non JSON valida: " . substr($response, 0, 1000));
}
return $data;
}
}
@@ -0,0 +1,135 @@
<?php
/**
* Mock implementation of VisualLimsApiClient.
* Activated when SIMULATE_EXPORT_LIMS=true in .env.
* All HTTP calls are skipped; fake but structurally valid data is returned.
* Every simulated call is logged via error_log() with a [SIMULATE] prefix.
*/
class VisualLimsApiClientMock
{
private int $fakeCommessaId;
public function __construct()
{
// Stable fake ID for the lifetime of a single request
$this->fakeCommessaId = mt_rand(90001, 99999);
error_log("[SIMULATE] VisualLimsApiClientMock initialised (fakeCommessaId={$this->fakeCommessaId})");
}
public function get(string $endpoint): array
{
error_log("[SIMULATE] GET {$endpoint}");
// --- Fixed-field dropdown lists ---
if (str_starts_with($endpoint, 'MoltiplicatorePrezzi')) {
return ['value' => [
['IdMoltiplicatorePrezzo' => 1, 'Codice' => 'MP-01', 'Descrizione' => 'Standard (1x)'],
['IdMoltiplicatorePrezzo' => 2, 'Codice' => 'MP-02', 'Descrizione' => 'Urgente (1.5x)'],
['IdMoltiplicatorePrezzo' => 3, 'Codice' => 'MP-03', 'Descrizione' => 'Extra Urgente (2x)'],
]];
}
if (str_starts_with($endpoint, 'AnagraficaCertestObject')) {
return ['value' => [
['IdAnagrafica' => 1, 'Codice' => 'OBJ-01', 'NomeAnagrafica' => 'Articolo Tessile'],
['IdAnagrafica' => 2, 'Codice' => 'OBJ-02', 'NomeAnagrafica' => 'Componente Meccanico'],
['IdAnagrafica' => 3, 'Codice' => 'OBJ-03', 'NomeAnagrafica' => 'Materiale Plastico'],
]];
}
if (str_starts_with($endpoint, 'AnagraficaCertestService')) {
return ['value' => [
['IdAnagrafica' => 1, 'Codice' => 'SRV-01', 'NomeAnagrafica' => 'Analisi Chimica'],
['IdAnagrafica' => 2, 'Codice' => 'SRV-02', 'NomeAnagrafica' => 'Test Meccanico'],
['IdAnagrafica' => 3, 'Codice' => 'SRV-03', 'NomeAnagrafica' => 'Prova Ambientale'],
]];
}
// Cliente? list — get_clienti.php exits early in simulate mode, but guard here too
if (str_starts_with($endpoint, 'Cliente?')) {
return ['value' => []];
}
// Cliente(N)?$expand=Responsabili
if (str_starts_with($endpoint, 'Cliente(')) {
preg_match('/Cliente\((\d+)\)/', $endpoint, $m);
$clienteId = isset($m[1]) ? (int) $m[1] : 0;
return [
'IdCliente' => $clienteId,
'Responsabili' => [
['IdClienteResponsabile' => 1, 'Nominativo' => 'Marco Bianchi'],
['IdClienteResponsabile' => 2, 'Nominativo' => 'Giulia Ferrari'],
['IdClienteResponsabile' => 3, 'Nominativo' => 'Andrea Russo'],
],
];
}
// --- CustomField dropdown values (get_customfield_values.php) ---
if (str_starts_with($endpoint, 'CustomField(')) {
preg_match('/CustomField\((\d+)\)/', $endpoint, $m);
$fieldId = isset($m[1]) ? (int) $m[1] : 0;
return [
'CustomFieldsValues' => [
['IdCustomFieldsValue' => $fieldId * 10 + 1, 'Valore' => 'Opzione A'],
['IdCustomFieldsValue' => $fieldId * 10 + 2, 'Valore' => 'Opzione B'],
['IdCustomFieldsValue' => $fieldId * 10 + 3, 'Valore' => 'Opzione C'],
],
];
}
// --- CommessaWeb OData calls (STEP 7 GET + STEP 10 verification) ---
preg_match('/\((\d+)\)/', $endpoint, $m);
$id = isset($m[1]) ? (int) $m[1] : $this->fakeCommessaId;
return [
'IdCommessa' => $id,
'CodiceCommessa' => "SIM-{$id}",
'CommesseCustomFields' => [], // Empty → PATCH step is skipped correctly
];
}
public function post(string $endpoint, array $payload): array
{
error_log("[SIMULATE] POST {$endpoint} payload=" . json_encode($payload));
// CommessaWeb creation
if ($endpoint === 'CommessaWeb') {
return [
'IdCommessa' => $this->fakeCommessaId,
'CodiceCommessa' => "SIM-{$this->fakeCommessaId}",
'Richiedente' => $payload['Richiedente'] ?? '',
'Descrizione' => $payload['Descrizione'] ?? '',
];
}
// Campione creation
if ($endpoint === 'Campione') {
return [
'IdCampione' => mt_rand(10001, 19999),
'Commessa' => $payload['Commessa'] ?? null,
'Matrice' => $payload['Matrice'] ?? null,
];
}
// InviaCommessa / ImportaCommessa (currently commented out upstream)
return ['simulated' => true, 'endpoint' => $endpoint];
}
public function patch(string $endpoint, array $payload): array
{
error_log("[SIMULATE] PATCH {$endpoint} payload=" . json_encode($payload));
return [];
}
public function postMultipart(string $endpoint, string $filePath, string $fileName, array $extraFields = []): array
{
error_log("[SIMULATE] POST multipart {$endpoint} file={$fileName}");
return ['simulated' => true, 'file' => $fileName];
}
}
@@ -0,0 +1,124 @@
<?php
require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
use Dotenv\Dotenv;
class VisualLimsApiClientXml
{
private static $instance = null;
private $baseUrl;
private $username;
private $password;
private $token = null;
private function __construct()
{
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
$dotenv->load();
$this->baseUrl = $_ENV['API_BASE_URL'];
$this->username = $_ENV['API_USERNAME'];
$this->password = $_ENV['API_PASSWORD'];
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new VisualLimsApiClientXml();
}
return self::$instance;
}
private function authenticate()
{
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'Username' => $this->username,
'Password' => $this->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);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_auth_debug_xml.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false || $http_code != 200) {
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
}
$token_data = json_decode($response, true);
$this->token = null;
if (is_array($token_data) && isset($token_data['token'])) {
$this->token = $token_data['token'];
} elseif (is_string($token_data) && !empty($token_data)) {
$this->token = trim($token_data, '"');
} elseif (is_string($response) && !empty($response)) {
$this->token = trim($response, '"');
}
if (empty($this->token)) {
throw new Exception("Token non ricevuto: " . substr($response, 0, 1000));
}
}
private function getToken()
{
if ($this->token === null) {
$this->authenticate();
}
return $this->token;
}
public function get($endpoint, $options = [])
{
$token = $this->getToken();
$query = http_build_query($options);
$url = "{$this->baseUrl}/api/odata/{$endpoint}" . ($query ? '?' . $query : '');
$ch = curl_init($url);
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_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_request_debug_xml.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta: {$curl_error}");
}
if ($http_code !== 200) {
throw new Exception("Errore nel recupero dati: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
// Verifica che la risposta sia XML
if (strpos($response, '<?xml') !== 0) {
throw new Exception("Risposta non valida: atteso formato XML, ricevuto: " . substr($response, 0, 1000));
}
return $response;
}
}
@@ -0,0 +1,124 @@
<?php
require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
use Dotenv\Dotenv;
class VisualLimsApiClientXml
{
private static $instance = null;
private $baseUrl;
private $username;
private $password;
private $token = null;
private function __construct()
{
$dotenv = Dotenv::createImmutable(dirname(__DIR__, 3));
$dotenv->load();
$this->baseUrl = $_ENV['API_BASE_URL'];
$this->username = $_ENV['API_USERNAME'];
$this->password = $_ENV['API_PASSWORD'];
}
public static function getInstance()
{
if (self::$instance === null) {
self::$instance = new VisualLimsApiClientXml();
}
return self::$instance;
}
private function authenticate()
{
$ch = curl_init("{$this->baseUrl}/api/authentication/authenticate");
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode([
'Username' => $this->username,
'Password' => $this->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);
curl_setopt($ch, CURLOPT_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_auth_debug_xml.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false || $http_code != 200) {
throw new Exception("Autenticazione fallita: HTTP {$http_code}, Errore cURL: {$curl_error}, Risposta: " . substr($response, 0, 1000));
}
$token_data = json_decode($response, true);
$this->token = null;
if (is_array($token_data) && isset($token_data['token'])) {
$this->token = $token_data['token'];
} elseif (is_string($token_data) && !empty($token_data)) {
$this->token = trim($token_data, '"');
} elseif (is_string($response) && !empty($response)) {
$this->token = trim($response, '"');
}
if (empty($this->token)) {
throw new Exception("Token non ricevuto: " . substr($response, 0, 1000));
}
}
private function getToken()
{
if ($this->token === null) {
$this->authenticate();
}
return $this->token;
}
public function get($endpoint, $options = [])
{
$token = $this->getToken();
$query = http_build_query($options);
$url = "{$this->baseUrl}/api/odata/{$endpoint}" . ($query ? '?' . $query : '');
$ch = curl_init($url);
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_VERBOSE, true);
$log = fopen(__DIR__ . '/curl_request_debug_xml.log', 'w') ?: fopen('php://stderr', 'w');
curl_setopt($ch, CURLOPT_STDERR, $log);
$response = curl_exec($ch);
$http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curl_error = curl_error($ch);
fclose($log);
curl_close($ch);
if ($response === false) {
throw new Exception("Errore nella richiesta: {$curl_error}");
}
if ($http_code !== 200) {
throw new Exception("Errore nel recupero dati: HTTP {$http_code}, Risposta: " . substr($response, 0, 1000));
}
// Verifica che la risposta sia XML
if (strpos($response, '<?xml') !== 0) {
throw new Exception("Risposta non valida: atteso formato XML, ricevuto: " . substr($response, 0, 1000));
}
return $response;
}
}
+3
View File
@@ -3,6 +3,9 @@ require_once dirname(__DIR__, 3) . '/vendor/autoload.php';
use Dotenv\Dotenv; use Dotenv\Dotenv;
Dotenv::createImmutable(dirname(__DIR__, 3))->safeLoad();
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
class DBHandlerSelect class DBHandlerSelect
{ {
private static $instance = null; private static $instance = null;
+133
View File
@@ -0,0 +1,133 @@
<?php
header('Content-Type: application/json');
include('include/headscript.php');
$dbHandler = DBHandlerSelect::getInstance();
$pdo = $dbHandler->getConnection();
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$data = json_decode(file_get_contents('php://input'), true);
$sourceIddatadb = isset($data['source_iddatadb']) ? (int)$data['source_iddatadb'] : 0;
$targetList = $data['target_iddatadb_list'] ?? [];
$targetIds = array_values(array_unique(array_filter(array_map('intval', (array)$targetList), function ($v) use ($sourceIddatadb) {
return $v > 0 && $v !== $sourceIddatadb;
})));
if ($sourceIddatadb <= 0 || empty($targetIds)) {
echo json_encode([
'success' => false,
'message' => 'Missing source or target records'
]);
exit;
}
try {
$pdo->beginTransaction();
// 1. Load source parts
$stmtParts = $pdo->prepare("
SELECT id, part_number, part_description, mix, idmatrice, note, dateexpiry
FROM identification_parts
WHERE iddatadb = ?
AND part_description IS NOT NULL
AND TRIM(part_description) <> ''
ORDER BY part_number ASC, id ASC
");
$stmtParts->execute([$sourceIddatadb]);
$sourceParts = $stmtParts->fetchAll(PDO::FETCH_ASSOC);
if (empty($sourceParts)) {
$pdo->rollBack();
echo json_encode([
'success' => false,
'message' => 'No parts found for source record'
]);
exit;
}
// 2. Prepare statements
$stmtInsertPart = $pdo->prepare("
INSERT INTO identification_parts
(iddatadb, part_number, part_description, mix, idmatrice, note, dateexpiry, created_at, updated_at)
VALUES
(:iddatadb, :part_number, :part_description, :mix, :idmatrice, :note, :dateexpiry, NOW(), NOW())
");
$stmtLoadCF = $pdo->prepare("
SELECT field_id, value_id, value_text
FROM identification_parts_customfields
WHERE part_id = ?
ORDER BY id ASC
");
$stmtInsertCF = $pdo->prepare("
INSERT INTO identification_parts_customfields
(part_id, field_id, value_id, value_text, created_at, updated_at)
VALUES
(:part_id, :field_id, :value_id, :value_text, NOW(), NOW())
");
$details = [];
$totalClonedParts = 0;
// 3. Clone source parts to each target record
foreach ($targetIds as $targetIddatadb) {
$clonedCountForTarget = 0;
foreach ($sourceParts as $part) {
$stmtInsertPart->execute([
':iddatadb' => $targetIddatadb,
':part_number' => $part['part_number'],
':part_description' => $part['part_description'],
':mix' => $part['mix'] ?? 'N',
':idmatrice' => $part['idmatrice'] !== '' ? $part['idmatrice'] : null,
':note' => $part['note'] ?? null,
':dateexpiry' => $part['dateexpiry'] ?? null,
]);
$newPartId = (int)$pdo->lastInsertId();
// Load source custom fields for this part
$stmtLoadCF->execute([(int)$part['id']]);
$customFields = $stmtLoadCF->fetchAll(PDO::FETCH_ASSOC);
foreach ($customFields as $cf) {
$stmtInsertCF->execute([
':part_id' => $newPartId,
':field_id' => (int)$cf['field_id'],
':value_id' => ($cf['value_id'] !== null && $cf['value_id'] !== '') ? (int)$cf['value_id'] : null,
':value_text' => $cf['value_text'] !== '' ? $cf['value_text'] : null,
]);
}
$clonedCountForTarget++;
$totalClonedParts++;
}
$details[] = [
'target_iddatadb' => $targetIddatadb,
'cloned_parts' => $clonedCountForTarget
];
}
$pdo->commit();
echo json_encode([
'success' => true,
'source_iddatadb' => $sourceIddatadb,
'cloned_targets' => count($targetIds),
'total_cloned_parts' => $totalClonedParts,
'details' => $details
]);
} catch (Throwable $e) {
if ($pdo->inTransaction()) {
$pdo->rollBack();
}
echo json_encode([
'success' => false,
'message' => 'Clone failed: ' . $e->getMessage()
]);
}
+228
View File
@@ -0,0 +1,228 @@
<?php
require_once 'class/db-functions.php';
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (!isset($_GET['id']) || !is_numeric($_GET['id'])) {
die("Invalid template ID");
}
$sourceTemplateId = (int)$_GET['id'];
try {
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$pdo->beginTransaction();
// 1. Get source template
$stmt = $pdo->prepare("
SELECT
name,
source_type,
header_row,
start_column,
description,
target_table,
sample_xlsx,
button_size,
button_bg_color,
button_text_color,
button_label,
status,
client_specific_fields,
idclient,
clientname,
schemaname,
idschema,
schemajson,
xls_headers,
api_sample_json,
json_nodes,
idroutine
FROM excel_templates
WHERE id = ?
LIMIT 1
");
$stmt->execute([$sourceTemplateId]);
$template = $stmt->fetch(PDO::FETCH_ASSOC);
if (!$template) {
throw new Exception("Template not found.");
}
// 2. Generate unique clone name
$baseName = 'Copia di ' . $template['name'];
$newName = $baseName;
$checkStmt = $pdo->prepare("SELECT COUNT(*) FROM excel_templates WHERE name = ?");
$checkStmt->execute([$newName]);
if ((int)$checkStmt->fetchColumn() > 0) {
$counter = 2;
do {
$newName = $baseName . ' (' . $counter . ')';
$checkStmt->execute([$newName]);
$exists = (int)$checkStmt->fetchColumn() > 0;
$counter++;
} while ($exists);
}
// 3. Insert cloned template
$insertTemplate = $pdo->prepare("
INSERT INTO excel_templates (
name,
source_type,
created_at,
updated_at,
header_row,
start_column,
description,
target_table,
sample_xlsx,
button_size,
button_bg_color,
button_text_color,
button_label,
status,
client_specific_fields,
idclient,
clientname,
schemaname,
idschema,
schemajson,
xls_headers,
api_sample_json,
json_nodes,
idroutine
) VALUES (
:name,
:source_type,
NOW(),
NOW(),
:header_row,
:start_column,
:description,
:target_table,
:sample_xlsx,
:button_size,
:button_bg_color,
:button_text_color,
:button_label,
:status,
:client_specific_fields,
:idclient,
:clientname,
:schemaname,
:idschema,
:schemajson,
:xls_headers,
:api_sample_json,
:json_nodes,
:idroutine
)
");
$insertTemplate->execute([
':name' => $newName,
':source_type' => $template['source_type'],
':header_row' => $template['header_row'],
':start_column' => $template['start_column'],
':description' => $template['description'],
':target_table' => $template['target_table'],
':sample_xlsx' => $template['sample_xlsx'],
':button_size' => $template['button_size'],
':button_bg_color' => $template['button_bg_color'],
':button_text_color' => $template['button_text_color'],
':button_label' => $template['button_label'],
':status' => $template['status'],
':client_specific_fields' => $template['client_specific_fields'],
':idclient' => $template['idclient'],
':clientname' => $template['clientname'],
':schemaname' => $template['schemaname'],
':idschema' => $template['idschema'],
':schemajson' => $template['schemajson'],
':xls_headers' => $template['xls_headers'],
':api_sample_json' => $template['api_sample_json'],
':json_nodes' => $template['json_nodes'],
':idroutine' => $template['idroutine']
]);
$newTemplateId = (int)$pdo->lastInsertId();
if ($newTemplateId <= 0) {
throw new Exception("Unable to create cloned template.");
}
// 4. Clone template_mapping rows
$cloneMappings = $pdo->prepare("
INSERT INTO template_mapping (
template_id,
schema_id,
field_id,
excel_column,
json_node,
is_manual,
manual_default,
auto_value,
data_type,
is_required,
default_value,
has_list,
length,
decimals,
min_value,
max_value,
default_curr_date,
tablename,
field_label,
main_field,
is_visible_import,
is_visible_parts
)
SELECT
:new_template_id,
schema_id,
field_id,
excel_column,
json_node,
is_manual,
manual_default,
auto_value,
data_type,
is_required,
default_value,
has_list,
length,
decimals,
min_value,
max_value,
default_curr_date,
tablename,
field_label,
main_field,
is_visible_import,
is_visible_parts
FROM template_mapping
WHERE template_id = :source_template_id
");
$cloneMappings->execute([
':new_template_id' => $newTemplateId,
':source_template_id' => $sourceTemplateId
]);
$pdo->commit();
header("Location: templates_dashboard.php?cloned=1&new_id=" . $newTemplateId);
exit;
} catch (Exception $e) {
if (isset($pdo) && $pdo->inTransaction()) {
$pdo->rollBack();
}
die("Clone error: " . htmlspecialchars($e->getMessage(), ENT_QUOTES, 'UTF-8'));
}
+147 -40
View File
@@ -7,7 +7,9 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Autenticazione e Recupero Clienti VisualLims</title> <title>Autenticazione VisualLims</title>
<!-- Includi Select2 CSS -->
<link href="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/css/select2.min.css" rel="stylesheet" />
<style> <style>
body { body {
font-family: Arial, sans-serif; font-family: Arial, sans-serif;
@@ -34,70 +36,175 @@
border: 1px solid #ccc; border: 1px solid #ccc;
word-wrap: break-word; word-wrap: break-word;
} }
#schemiResult {
margin-top: 20px;
padding: 10px;
border: 1px solid #ccc;
word-wrap: break-word;
}
.select2-container {
width: 100% !important;
}
</style> </style>
</head> </head>
<body> <body>
<h1>Autenticazione e Recupero Clienti VisualLims</h1> <h1>Autenticazione VisualLims</h1>
<button id="authButton">Autentica e Recupera Clienti</button> <button id="authButton">Autentica</button>
<div id="result"></div> <div id="result"></div>
<!-- Tendina per i clienti -->
<h3>Seleziona un cliente:</h3>
<select id="clientiSelect" style="width: 100%;">
<option value="">Seleziona un cliente...</option>
</select>
<!-- Area per mostrare gli schemi -->
<div id="schemiResult"></div>
<!-- Includi jQuery (necessario per Select2) -->
<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
<!-- Includi Select2 JS -->
<script src="https://cdn.jsdelivr.net/npm/select2@4.1.0-rc.0/dist/js/select2.min.js"></script>
<script> <script>
// Inizializza Select2 sulla tendina
$(document).ready(function() {
$('#clientiSelect').select2({
placeholder: "Cerca un cliente...",
allowClear: true
});
// Carica i clienti al caricamento della pagina
loadClienti();
});
// Autenticazione
document.getElementById('authButton').addEventListener('click', async () => { document.getElementById('authButton').addEventListener('click', async () => {
const resultDiv = document.getElementById('result'); const resultDiv = document.getElementById('result');
resultDiv.textContent = 'Autenticazione e recupero clienti in corso...'; resultDiv.textContent = 'Autenticazione in corso...';
try { try {
// Step 1: Autenticazione const response = await fetch('auth_proxy.php', {
const authResponse = await fetch('auth_proxy.php', {
method: 'POST', method: 'POST',
headers: { headers: {
'Content-Type': 'application/json', 'Content-Type': 'application/json',
} }
}); });
const authData = await authResponse.json(); const data = await response.json();
if (!authResponse.ok) { if (!response.ok) {
throw new Error(`Errore HTTP durante autenticazione! Stato: ${authResponse.status}, Dettagli: ${authData.error || JSON.stringify(authData)}`); throw new Error(`Errore HTTP! Stato: ${response.status}, Dettagli: ${data.error || 'Nessun dettaglio disponibile'}`);
} }
// Estrai il token if (typeof data === 'string' && data.length > 0) {
let token; resultDiv.textContent = `Token: ${data}`;
if (authData.token && typeof authData.token === 'string' && authData.token.length > 0) { } else if (data && data.token) {
token = authData.token; resultDiv.textContent = `Token: ${data.token}`;
} else { } else {
throw new Error(`Autenticazione fallita: Nessun token valido ricevuto. Dettagli: ${JSON.stringify(authData)}`); resultDiv.textContent = `Autenticazione fallita: Nessun token ricevuto. Dettagli: ${JSON.stringify(data)}`;
}
// Step 2: Recupero elenco clienti
const clientiResponse = await fetch(`https://93.43.5.102/limsapi/api/odata/Cliente`, {
method: 'GET',
headers: {
'Authorization': `Bearer ${token}`,
'Accept': 'application/json',
'User-Agent': 'Mozilla/5.0 (compatible; PHP cURL)'
}
});
const clientiData = await clientiResponse.json();
if (!clientiResponse.ok) {
throw new Error(`Errore HTTP durante recupero clienti! Stato: ${clientiResponse.status}, Dettagli: ${clientiData.error || JSON.stringify(clientiData)}`);
}
// Mostra l'elenco dei clienti
if (clientiData && clientiData.value && clientiData.value.length > 0) {
const clientiList = clientiData.value.map(cliente => ({
IdCliente: cliente.IdCliente,
NomeCliente: cliente.Nome || 'N/A'
}));
resultDiv.textContent = `Clienti trovati:\n${JSON.stringify(clientiList, null, 2)}`;
} else {
resultDiv.textContent = `Nessun cliente trovato. Dettagli: ${JSON.stringify(clientiData)}`;
} }
} catch (error) { } catch (error) {
resultDiv.textContent = `Errore: ${error.message}`; resultDiv.textContent = `Errore: ${error.message}`;
} }
}); });
// Funzione per caricare i clienti nella tendina
async function loadClienti() {
const resultDiv = document.getElementById('result');
resultDiv.textContent = 'Caricamento clienti...';
try {
const response = await fetch('get_clienti.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `Errore HTTP: ${response.status}, Dettagli: ${JSON.stringify(data)}`);
}
if (data.value && Array.isArray(data.value)) {
const select = document.getElementById('clientiSelect');
data.value.forEach(c => {
const nome = c.Nominativo || 'Nome non disponibile';
const id = c.IdCliente || 'ID non disponibile';
const option = new Option(`${nome.trim()} (ID: ${id})`, id);
select.add(option);
});
resultDiv.textContent = 'Clienti caricati con successo.';
} else {
resultDiv.textContent = 'Nessun cliente trovato o formato dati non valido.';
console.log('Risposta API:', data);
}
} catch (err) {
resultDiv.textContent = 'Errore: ' + err.message;
console.error('Dettagli errore:', err);
}
}
// Evento per gestire la selezione di un cliente e recuperare gli schemi
$('#clientiSelect').on('select2:select', async function(e) {
const clienteId = e.target.value; // Oppure: $(this).val()
const schemiDiv = document.getElementById('schemiResult'); // Correzione del nome variabile
// Log per debug
console.log('Cliente selezionato:', clienteId);
if (!clienteId) {
schemiDiv.textContent = '';
return;
}
schemiDiv.textContent = 'Caricamento schemi...';
try {
const response = await fetch('get_schemi.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
clienteId
})
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || `Errore HTTP: ${response.status}, Dettagli: ${JSON.stringify(data)}`);
}
if (data.SchemiAbilitati && Array.isArray(data.SchemiAbilitati)) {
let html = '<h3>Schemi Abilitati:</h3><ul>';
data.SchemiAbilitati.forEach(s => {
const nomeSchema = s.NomeSchema || s.Descrizione || 'Schema non specificato';
html += `<li>${nomeSchema}</li>`;
});
html += '</ul>';
schemiDiv.innerHTML = html;
} else {
schemiDiv.textContent = 'Nessuno schema trovato per questo cliente.';
console.log('Risposta Schemi:', data);
}
} catch (err) {
schemiDiv.textContent = 'Errore: ' + err.message;
console.error('Dettagli errore:', err);
}
});
// Gestisci la deselezione (opzionale)
$('#clientiSelect').on('select2:unselect', function(e) {
const schemiDiv = document.getElementById('schemiResult'); // Correzione del nome variabile
schemiDiv.textContent = '';
});
</script> </script>
</body> </body>
+79
View File
@@ -0,0 +1,79 @@
<?php
header('Content-Type: application/json');
require_once(__DIR__ . '/include/headscript.php');
$db = DBHandlerSelect::getInstance();
$pdo = $db->getConnection();
$input = json_decode(file_get_contents('php://input'), true);
$templateId = (int)($input['template_id'] ?? 0);
if ($templateId <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid template_id']);
exit;
}
// If already exists, do nothing
$stmt = $pdo->prepare("SELECT COUNT(*) FROM template_fixed_mapping WHERE template_id = ?");
$stmt->execute([$templateId]);
if ((int)$stmt->fetchColumn() > 0) {
echo json_encode(['success' => true, 'created' => 0, 'message' => 'Fixed fields already exist']);
exit;
}
/**
* FIXED FIELDS STANDARD (no UI selection)
* is_manual always 1
* is_visible_import default 1
*/
$fixedFields = [
// fixed_field_key, data_type
['ClienteResponsabile', 'INT'],
['ClienteFornitore', 'INT'],
['ClienteAnalisi', 'INT'],
['MoltiplicatorePrezzo', 'INT'],
['AnagraficaCertestObject', 'INT'],
['AnagraficaCertestService', 'INT'],
['ConsegnaRichiesta', 'DATE'],
];
try {
$pdo->beginTransaction();
$ins = $pdo->prepare("
INSERT INTO template_fixed_mapping
(template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import)
VALUES
(:template_id, :fixed_field_key, 1, :data_type, 1, NULL, 1)
");
foreach ($fixedFields as $f) {
$ins->execute([
':template_id' => $templateId,
':fixed_field_key' => $f[0],
':data_type' => $f[1],
]);
}
$pdo->commit();
// Return rows (for client render)
$stmt = $pdo->prepare("
SELECT id, template_id, fixed_field_key, is_manual, data_type, is_required, default_value, is_visible_import
FROM template_fixed_mapping
WHERE template_id = ?
ORDER BY id ASC
");
$stmt->execute([$templateId]);
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
echo json_encode(['success' => true, 'created' => count($rows), 'rows' => $rows]);
} catch (Exception $e) {
if ($pdo->inTransaction()) $pdo->rollBack();
echo json_encode(['success' => false, 'message' => $e->getMessage()]);
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,105 @@
<?php
require_once dirname(__DIR__, 3) . '/vendor/autoload.php'; // Risale a root/vendor/
require_once dirname(__FILE__) . '/../class/VisualLimsApiClient.class.php'; // In root/public/userarea/class/
use Dotenv\Dotenv;
// Debug: Log the path where we expect the .env file
$envPath = dirname(__DIR__, 3);
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Expected .env path: ' . $envPath . PHP_EOL, FILE_APPEND);
// Carica il file .env dalla root del progetto
try {
$dotenv = Dotenv::createImmutable($envPath);
$dotenv->load();
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore caricamento .env: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
exit(1);
}
date_default_timezone_set($_ENV['APP_TIMEZONE'] ?? 'Europe/Rome');
// Recupera le variabili d'ambiente
$dbHost = $_ENV['DB_HOST'];
$dbName = $_ENV['DB_DATABASE'];
$dbUser = $_ENV['DB_USERNAME'];
$dbPass = $_ENV['DB_PASSWORD'];
$dbPrefix = $_ENV['DB_PREFIX'];
// Debug: Log database connection details (excluding password)
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . " - DB Connection: host=$dbHost, dbname=$dbName, user=$dbUser, prefix=$dbPrefix" . PHP_EOL, FILE_APPEND);
// Connessione al database MySQL
try {
$pdo = new PDO("mysql:host=$dbHost;dbname=$dbName;charset=utf8mb4", $dbUser, $dbPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - Errore connessione DB: ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
exit(1);
}
try {
$api = VisualLimsApiClient::getInstance();
// Endpoint per recuperare le Matrici
$endpoint = 'Matrice';
// (Opzionale) aggiungi parametri
$options = []; // es. ['$top' => 100]
// Debug: salva URL usato
$base_url = 'https://93.43.5.102/limsapi/api/odata/';
$query = http_build_query($options);
$full_url = $base_url . $endpoint . ($query ? '?' . $query : '');
file_put_contents(__DIR__ . '/last_url.txt', $full_url . PHP_EOL, FILE_APPEND);
// Chiamata API
$data = $api->get($endpoint, $options);
// Debug: Log the API response size
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - API response received, value count: ' . (isset($data['value']) ? count($data['value']) : 0) . PHP_EOL, FILE_APPEND);
// Salva il JSON in locale (opzionale, per debug)
file_put_contents(__DIR__ . '/matrici_response.json', json_encode($data, JSON_PRETTY_PRINT));
// Svuota la tabella (dump rapido)
$pdo->exec("TRUNCATE TABLE {$dbPrefix}matrici");
// Debug: Log after truncate
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Table truncated: ' . $dbPrefix . 'matrici' . PHP_EOL, FILE_APPEND);
// Prepara l'insert
$stmt = $pdo->prepare("
INSERT INTO {$dbPrefix}matrici (
IdMatrice, NomeMatriceTraduzione, DescrizioneTraduzione, MacroMatrice, NomeMatrice, Descrizione
) VALUES (
:IdMatrice, :NomeMatriceTraduzione, :DescrizioneTraduzione, :MacroMatrice, :NomeMatrice, :Descrizione
)
");
// Inserisci i dati
$insertedRows = 0;
if (isset($data['value']) && is_array($data['value'])) {
foreach ($data['value'] as $item) {
$stmt->execute([
':IdMatrice' => $item['IdMatrice'],
':NomeMatriceTraduzione' => $item['NomeMatriceTraduzione'],
':DescrizioneTraduzione' => $item['DescrizioneTraduzione'] ?? null,
':MacroMatrice' => $item['MacroMatrice'] ?? null,
':NomeMatrice' => $item['NomeMatrice'],
':Descrizione' => $item['Descrizione'] ?? null,
]);
$insertedRows++;
// Debug: Log each inserted row
file_put_contents(__DIR__ . '/debug_log.txt', date('Y-m-d H:i:s') . ' - Inserted row with IdMatrice: ' . $item['IdMatrice'] . PHP_EOL, FILE_APPEND);
}
}
// Log successo
file_put_contents(__DIR__ . '/success_log.txt', date('Y-m-d H:i:s') . ' - Aggiornamento completato: ' . $insertedRows . ' record inseriti.' . PHP_EOL, FILE_APPEND);
} catch (Exception $e) {
file_put_contents(__DIR__ . '/error_log.txt', date('Y-m-d H:i:s') . ' - ' . $e->getMessage() . PHP_EOL, FILE_APPEND);
exit(1);
}
exit(0); // Esci con successo per cron
+1
View File
@@ -0,0 +1 @@
https://93.43.5.102/limsapi/api/odata/Matrice
File diff suppressed because it is too large Load Diff
@@ -0,0 +1 @@
2026-04-03 10:07:01 - Aggiornamento completato: 3198 record inseriti.
-302
View File
@@ -1,302 +0,0 @@
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: iftm.it]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: iftm.it
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:34:52 GMT
<
* Connection #0 to host 93.43.5.102 left intact
* Trying 83.149.157.58:443...
* Connected to iftm.it (83.149.157.58) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: CN=*.iftm.it
* start date: Oct 8 07:42:11 2024 GMT
* expire date: Oct 8 07:42:11 2025 GMT
* issuer: C=IT; ST=Bergamo; L=Ponte San Pietro; O=Actalis S.p.A.; CN=Actalis Domain Validation Server CA G3
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://iftm.it/visuallimswebapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: iftm.it]
* [HTTP/2] [1] [:path: /visuallimswebapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 64]
> POST /visuallimswebapi/api/authentication/authenticate HTTP/2
Host: iftm.it
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 64
< HTTP/2 401
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< date: Tue, 03 Jun 2025 09:41:50 GMT
<
* Connection #0 to host iftm.it left intact
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: 93.43.5.102
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:43:41 GMT
<
* Connection #0 to host 93.43.5.102 left intact
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: 93.43.5.102
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:45:25 GMT
<
* Connection #0 to host 93.43.5.102 left intact
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: 93.43.5.102
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:47:45 GMT
<
* Connection #0 to host 93.43.5.102 left intact
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: 93.43.5.102
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:48:27 GMT
<
* Connection #0 to host 93.43.5.102 left intact
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: 93.43.5.102
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:50:49 GMT
<
* Connection #0 to host 93.43.5.102 left intact
* Trying 93.43.5.102:443...
* Connected to 93.43.5.102 (93.43.5.102) port 443
* ALPN: curl offers h2,http/1.1
* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
* ALPN: server accepted h2
* Server certificate:
* subject: C=FR; ST=Île-de-France; O=Bureau Veritas; CN=bvcpsitaly-elims.it
* start date: Feb 17 00:00:00 2025 GMT
* expire date: Feb 17 23:59:59 2026 GMT
* issuer: C=US; O=Corporation Service Company; CN=Corporation Service Company RSA OV SSL CA
* SSL certificate verify result: unable to get local issuer certificate (20), continuing anyway.
* using HTTP/2
* [HTTP/2] [1] OPENED stream for https://93.43.5.102/limsapi/api/authentication/authenticate
* [HTTP/2] [1] [:method: POST]
* [HTTP/2] [1] [:scheme: https]
* [HTTP/2] [1] [:authority: 93.43.5.102]
* [HTTP/2] [1] [:path: /limsapi/api/authentication/authenticate]
* [HTTP/2] [1] [content-type: application/json]
* [HTTP/2] [1] [accept: application/json]
* [HTTP/2] [1] [user-agent: Mozilla/5.0 (compatible; PHP cURL)]
* [HTTP/2] [1] [content-length: 51]
> POST /limsapi/api/authentication/authenticate HTTP/2
Host: 93.43.5.102
Content-Type: application/json
Accept: application/json
User-Agent: Mozilla/5.0 (compatible; PHP cURL)
Content-Length: 51
< HTTP/2 200
< cache-control: max-age=0
< content-type: application/json; charset=utf-8
< server: Microsoft-IIS/10.0
< strict-transport-security: max-age=2592000
< x-powered-by: ASP.NET
< x-content-type-options: nosniff
< date: Tue, 03 Jun 2025 09:55:50 GMT
<
* Connection #0 to host 93.43.5.102 left intact
-18
View File
@@ -1,18 +0,0 @@
HTTP Code: 200
Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc0ODk1MTAyMSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.KntKk8Up-9owFjy7onziK2BfncEnSNhizyNX9S-QHaw"
HTTP Code: 200
Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc0ODk1MTEyNSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.IWjzzBg0kQ3lq4aK2ByMlY7Bwzb7Qq-6ziXV8kkZ2J0"
HTTP Code: 200
Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc0ODk1MTI2NSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.UgVz6PTF9dmbFYdgyRZS0TcI0pvLEIQ3yMjPiicF1vs"
HTTP Code: 200
Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc0ODk1MTMwNywiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.PolfU_FWuVMd-YfonBYTo0i0qIl6kn6nUkCWCEBvZuA"
HTTP Code: 200
Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc0ODk1MTQ0OSwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.CGZ-9KbMmW19JYTVGfjIcbNscSsGB4dwoHCJkHHs_4M"
HTTP Code: 200
Response: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9uYW1laWRlbnRpZmllciI6IjQ5MiIsIlhhZlNlY3VyaXR5QXV0aFBhc3NlZCI6IlhhZlNlY3VyaXR5QXV0aFBhc3NlZCIsImh0dHA6Ly9zY2hlbWFzLnhtbHNvYXAub3JnL3dzLzIwMDUvMDUvaWRlbnRpdHkvY2xhaW1zL25hbWUiOiJXZWJBcGlVc2VyIiwiWGFmU2VjdXJpdHkiOiJYYWZTZWN1cml0eSIsIlhhZkxvZ29uUGFyYW1zIjoicTFZS0xVNHQ4a3ZNVFZXeVVncFBUWElzeUFRSktPa29CU1FXRjVmbkY2VUF4Y3RUa3hJTE1rdUI0Z2FHU3JVQSIsImV4cCI6MTc0ODk1MTc1MCwiaXNzIjoiTXkiLCJhdWQiOiJodHRwOi8vbG9jYWxob3N0OjQyMDAifQ.FCAm5sWed1Q-QiIsctMgSBfEd4sfN3kfVR3Mqd3XQz0"

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