<?php
namespace App\AI\Contexts\Entity; class Supplier extends \App\AI\Contexts\Entity\EntityAbstract { public function getDeveloperMessages(): array { $developerMessages = []; $developerMessages[] = "Never show internal IDs such as draft_id, tool call IDs or storage paths to the user unless explicitly requested.
            Use natural references like \"this draft\" or \"the supplier draft\"."; $developerMessages[] = "Supplier blocking (lock) workflow:
            - Never call block_supplier from the first user request.
            - First call preview_supplier_block using the supplier_id (and optionally blocking_reason).
            - Show the returned review_text and ask for a blocking reason if missing.
            - Ask the user to confirm with the exact confirmation_phrase.
            - Only call block_supplier when the user provides a confirmation phrase that contains CONFIRM and a non-empty blocking_reason.
            - If can_block=false, explain why and do not ask for confirmation."; $developerMessages[] = "Supplier unblocking (unlock) workflow:
            - Never call unblock_supplier from the first user request.
            - First call preview_supplier_unblock using the supplier_id.
            - Show the returned review_text and ask the user to confirm with the exact confirmation_phrase.
            - Only call unblock_supplier when the user provides a confirmation phrase that contains CONFIRM.
            - If can_unblock=false, explain why and do not ask for confirmation."; return $developerMessages; } protected $entityFields = [ 'companyPrefix', 'company', 'companySuffix', 'alias', 'category', 'fiscalCode', 'regNo', 'vatRegistered', 'vatCertificateNumber', ]; protected $mandatoryFields = ['company', 'alias', ]; protected function checkPermission_create_supplier_draft() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-insert'); } protected function checkPermission_update_supplier_draft() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-insert'); } protected function checkPermission_preview_supplier() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-insert'); } protected function checkPermission_submit_supplier() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-insert'); } protected function checkPermission_preview_supplier_block() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-lock'); } protected function checkPermission_block_supplier() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-lock'); } protected function checkPermission_preview_supplier_unblock() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-lock'); } protected function checkPermission_unblock_supplier() : bool { return \App\UserAccess\CurrentUserAccessFast::getInstance()->hasAccessToOption('Modules/Partners/Suppliers-lock'); } protected function generateFunctionObject_create_supplier_draft(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj): \App\AI\Api\ChatGpt\Responses\ToolFunction { $funcObj = new \App\AI\Api\ChatGpt\Responses\ToolFunction( "create_supplier_draft", "Create a new supplier draft. This does NOT post/commit the supplier. It only stores a draft and returns what is missing.", false ); $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $this->populateFunctionInsertUpdateParameters($paramsObj); $funcObj->setParameters($paramsObj); return $funcObj; } protected function generateFunctionObject_update_supplier_draft(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj): \App\AI\Api\ChatGpt\Responses\ToolFunction { $funcObj = new \App\AI\Api\ChatGpt\Responses\ToolFunction( "update_supplier_draft", "Update an existing supplier draft with new or corrected fields. Does NOT commit the supplier.", false ); $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $draftIdObj = new \App\AI\Api\ChatGpt\Responses\Property(); $draftIdObj->type = "integer"; $draftIdObj->description = "The draft ID returned when the draft was created."; $paramsObj->addProperty("draft_id", $draftIdObj); $patchObj = new \App\AI\Api\ChatGpt\Responses\Property(); $patchObj->type = "object"; $patchObj->description = "An object with the fields to update in the draft."; $this->populateFunctionInsertUpdateParameters($patchObj); $paramsObj->addProperty("patch", $patchObj); $paramsObj->setRequired(['draft_id', 'patch']); return $funcObj; } protected function generateFunctionObject_preview_supplier(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj): \App\AI\Api\ChatGpt\Responses\ToolFunction { $funcObj = new \App\AI\Api\ChatGpt\Responses\ToolFunction( "preview_supplier", "Return a human-friendly preview of the supplier draft (all the fields) for user review. Does NOT commit.", false ); $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $draftIdObj = new \App\AI\Api\ChatGpt\Responses\Property(); $draftIdObj->type = "integer"; $paramsObj->addProperty("draft_id", $draftIdObj); $paramsObj->setRequired(['draft_id']); return $funcObj; } protected function generateFunctionObject_submit_supplier(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj): \App\AI\Api\ChatGpt\Responses\ToolFunction { $funcObj = new \App\AI\Api\ChatGpt\Responses\ToolFunction( "submit_supplier", "Commit/post the supplier draft into the system. Only call this after user explicitly confirms.", false ); $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $draftIdObj = new \App\AI\Api\ChatGpt\Responses\Property(); $draftIdObj->type = "integer"; $paramsObj->addProperty("draft_id", $draftIdObj); $confirmObj = new \App\AI\Api\ChatGpt\Responses\Property(); $confirmObj->type = "string"; $confirmObj->description = "Must be the exact user confirmation phrase: 'CONFIRM'."; $paramsObj->addProperty("user_confirmation_text", $confirmObj); $paramsObj->setRequired(['draft_id', 'user_confirmation_text']); return $funcObj; } protected function populateFunctionInsertUpdateParameters( \App\AI\Api\ChatGpt\Responses\Parameters|\App\AI\Api\ChatGpt\Responses\Property $paramsObj ) : \App\AI\Api\ChatGpt\Responses\Parameters|\App\AI\Api\ChatGpt\Responses\Property { $CFG = \Engine\Loader_Cfg::getInstance()->getGlobal(); $prefObj = new \App\AI\Api\ChatGpt\Responses\Property(); $prefObj->type = "string"; $prefObj->description = "Supplier company prefix."; $prefObj->enum = array_values(array_filter($CFG['generalData']['companyPrefix'])); $paramsObj->addProperty("companyPrefix", $prefObj); $companyObj = new \App\AI\Api\ChatGpt\Responses\Property(); $companyObj->type = "string"; $companyObj->description = "Supplier company name."; $paramsObj->addProperty("company", $companyObj); $suffObj = new \App\AI\Api\ChatGpt\Responses\Property(); $suffObj->type = "string"; $suffObj->description = "Supplier company suffix."; $suffObj->enum = array_values(array_filter($CFG['generalData']['companySuffix'])); $paramsObj->addProperty("companySuffix", $suffObj); $aliasObj = new \App\AI\Api\ChatGpt\Responses\Property(); $aliasObj->type = "string"; $aliasObj->description = "Supplier alias."; $paramsObj->addProperty("alias", $aliasObj); $categoryObj = new \App\AI\Api\ChatGpt\Responses\Property(); $categoryObj->type = "string"; $categoryObj->description = "Supplier category."; $categoryObj->enum = array_values($CFG['suppliers']['possibleCategories']); $paramsObj->addProperty("category", $categoryObj); $fiscObj = new \App\AI\Api\ChatGpt\Responses\Property(); $fiscObj->type = "string"; $fiscObj->description = "Supplier fiscal code."; $paramsObj->addProperty("fiscalCode", $fiscObj); $regObj = new \App\AI\Api\ChatGpt\Responses\Property(); $regObj->type = "string"; $regObj->description = "Supplier commerce registry."; $paramsObj->addProperty("regNo", $regObj); $vatRegObj = new \App\AI\Api\ChatGpt\Responses\Property(); $vatRegObj->type = "boolean"; $vatRegObj->description = "Is the supplier VAT registered?"; $paramsObj->addProperty("vatRegistered", $vatRegObj); $vatCertObj = new \App\AI\Api\ChatGpt\Responses\Property(); $vatCertObj->type = "string"; $vatCertObj->description = "Supplier VAT certificate number."; $paramsObj->addProperty("vatCertificateNumber", $vatCertObj); return $paramsObj; } protected function generateFunctionObject_preview_supplier_block(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj) : \App\AI\Api\ChatGpt\Responses\ToolFunction { $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $suppObj = new \App\AI\Api\ChatGpt\Responses\Property(); $suppObj->type = "integer"; $suppObj->description = "The unique ID of the supplier to block (lock)."; $paramsObj->addProperty("supplier_id", $suppObj); $reasObj = new \App\AI\Api\ChatGpt\Responses\Property(); $reasObj->type = "string"; $reasObj->description = "Optional blocking reason for the preview summary. Required before executing block_supplier."; $paramsObj->addProperty("blocking_reason", $reasObj); $paramsObj->required = ["supplier_id", ]; return $funcObj; } protected function generateFunctionObject_block_supplier(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj) : \App\AI\Api\ChatGpt\Responses\ToolFunction { $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $suppObj = new \App\AI\Api\ChatGpt\Responses\Property(); $suppObj->type = "integer"; $suppObj->description = "The unique ID of the supplier to block (lock)."; $paramsObj->addProperty("supplier_id", $suppObj); $reasObj = new \App\AI\Api\ChatGpt\Responses\Property(); $reasObj->type = "string"; $reasObj->description = "Blocking reason recorded on the supplier (locked comments)."; $paramsObj->addProperty("blocking_reason", $reasObj); $confirmObj = new \App\AI\Api\ChatGpt\Responses\Property(); $confirmObj->type = "string"; $confirmObj->description = "Must be the exact user confirmation phrase: 'CONFIRM'."; $paramsObj->addProperty("user_confirmation_text", $confirmObj); $paramsObj->required = ["supplier_id", "blocking_reason", "user_confirmation_text", ]; return $funcObj; } protected function generateFunctionObject_preview_supplier_unblock(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj) : \App\AI\Api\ChatGpt\Responses\ToolFunction { $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $suppObj = new \App\AI\Api\ChatGpt\Responses\Property(); $suppObj->type = "integer"; $suppObj->description = "The unique ID of the blocked supplier to unblock (unlock)."; $paramsObj->addProperty("supplier_id", $suppObj); $paramsObj->required = ["supplier_id", ]; return $funcObj; } protected function generateFunctionObject_unblock_supplier(\App\AI\Api\ChatGpt\Responses\ToolFunction $funcObj) : \App\AI\Api\ChatGpt\Responses\ToolFunction { $paramsObj = new \App\AI\Api\ChatGpt\Responses\Parameters(); $funcObj->setParameters($paramsObj); $suppObj = new \App\AI\Api\ChatGpt\Responses\Property(); $suppObj->type = "integer"; $suppObj->description = "The unique ID of the blocked supplier to unblock (unlock)."; $paramsObj->addProperty("supplier_id", $suppObj); $confirmObj = new \App\AI\Api\ChatGpt\Responses\Property(); $confirmObj->type = "string"; $confirmObj->description = "Must be the exact user confirmation phrase: 'CONFIRM'."; $paramsObj->addProperty("user_confirmation_text", $confirmObj); $paramsObj->required = ["supplier_id", "user_confirmation_text", ]; return $funcObj; } protected function executeFunction_create_supplier_draft(string $arguments) : array { $args = json_decode($arguments, 1); return $this->getCreateEntityDraft($args); } protected function executeFunction_update_supplier_draft(string $arguments) : array { $args = json_decode($arguments, 1); return $this->getUpdateEntityDraft($args); } protected function executeFunction_preview_supplier(string $arguments) : array { $args = json_decode($arguments, 1); return $this->getPreviewEntityDraft($args); } protected function executeFunction_submit_supplier(string $arguments) : array { $args = json_decode($arguments, 1); $draft = $this->getPreviewEntityDraft($args); $result = []; if ($args['user_confirmation_text']) { try { \DB::startTransaction(); $suppEnt = $this->createSupplierEntityFromDraft($draft['fields']); \DB::commitTransaction(); $draft['fields']['id'] = $suppEnt->getId(); $result['draft_id'] = $args['draft_id']; $result['idSupplier'] = $suppEnt->getId(); $result['message'] = 'Supplier committed successfully.'; } catch (\App\Entity\Exception $ex) { \DB::rollbackTransaction(); $result['draft_id'] = $args['draft_id']; $result['error'] = $ex->getMessageBulk(); } } else { $result['draft_id'] = $args['draft_id']; $result['error'] = 'Supplier was not committed, confirmation not received.'; } return $result; } protected function executeFunction_preview_supplier_block(string $arguments) : array { $args = json_decode($arguments, 1); $results = []; $suppEnt = \Entity\Supplier::select($args['supplier_id']); if ($suppEnt->recordExists()) { $returnFields = ['id', 'locked', 'lockedComments', 'company', 'alias', ]; foreach ($returnFields as $field) { $results['supplier'][$field] = $suppEnt->$field; } if ($suppEnt->locked) { $results['can_block'] = false; $results['error'] = "Supplier is already blocked."; } else { $entity = \Entity\Supplier::update($args['supplier_id']); $msgs = new \App\MessagesCollector(); $results['can_block'] = $this->determineOperationAvailability($entity, $msgs); if ($msgs->hasErrorOrWarning()) { $results['error'] = $msgs->getMessageBulk(); } else { $results['review_text'] = sprintf( "You are about to block supplier %s (ID %s). Blocking reason: %s", $suppEnt->companyFull, $suppEnt->getId(), $args['blocking_reason'] ); $results['confirmation_phrase'] = sprintf("CONFIRM BLOCK SUPPLIER %s", $suppEnt->getId()); } } } else { $results['error'] = "Supplier not found"; } return $results; } protected function executeFunction_block_supplier(string $arguments) : array { $args = json_decode($arguments, 1); $results = []; if ($args['user_confirmation_text']) { try { \DB::startTransaction(); $suppUpd = \Entity\Supplier::update($args['supplier_id']); $suppUpd->locked = 1; $suppUpd->lockedComments = $args['blocking_reason']; $suppUpd->save(); \DB::commitTransaction(); $results['message'] = sprintf("Successfully blocked supplier ID %s.", $args['supplier_id']); } catch (\App\Entity\Exception $ex) { \DB::rollbackTransaction(); $results['message'] = $ex->getMessagesCollector()->getMessageBulk(); } } else { $results['supplier_id'] = $args['supplier_id']; $results['error'] = 'Supplier was not blocked.'; } return $results; } protected function executeFunction_preview_supplier_unblock(string $arguments) : array { $args = json_decode($arguments, 1); $results = []; $suppEnt = \Entity\Supplier::select($args['supplier_id']); if ($suppEnt->recordExists()) { $returnFields = ['id', 'locked', 'lockedComments', 'company', 'alias', ]; foreach ($returnFields as $field) { $results['supplier'][$field] = $suppEnt->$field; } if (!$suppEnt->locked) { $results['can_unblock'] = false; $results['error'] = "Supplier is not blocked."; } else { $entity = \Entity\Supplier::update($args['supplier_id']); $msgs = new \App\MessagesCollector(); $results['can_unblock'] = $this->determineOperationAvailability($entity, $msgs); if ($msgs->hasErrorOrWarning()) { $results['error'] = $msgs->getMessageBulk(); } else { $lockNote = trim((string) $suppEnt->lockedComments); $results['review_text'] = sprintf( "You are about to unblock supplier %s (ID %s).%s", $suppEnt->companyFull, $suppEnt->getId(), $lockNote !== '' ? sprintf(' Previous lock note: %s.', $lockNote) : '' ); $results['confirmation_phrase'] = sprintf("CONFIRM UNBLOCK SUPPLIER %s", $suppEnt->getId()); } } } else { $results['error'] = "Supplier not found"; } return $results; } protected function executeFunction_unblock_supplier(string $arguments) : array { $args = json_decode($arguments, 1); $results = []; if ($args['user_confirmation_text']) { try { \DB::startTransaction(); $suppUpd = \Entity\Supplier::update($args['supplier_id']); $suppUpd->locked = 0; $suppUpd->save(); \DB::commitTransaction(); $results['message'] = sprintf("Successfully unblocked supplier ID %s.", $args['supplier_id']); } catch (\App\Entity\Exception $ex) { \DB::rollbackTransaction(); $results['message'] = $ex->getMessagesCollector()->getMessageBulk(); } } else { $results['supplier_id'] = $args['supplier_id']; $results['error'] = 'Supplier was not unblocked.'; } return $results; } protected function createSupplierEntityFromDraft(array $fields): \Entity\Supplier { $suppEnt = \Entity\Supplier::insert(); if ($fields['companyPrefix']) { $suppEnt->companyPrefix = mb_strtoupper($fields['companyPrefix']); } $suppEnt->company = mb_strtoupper($fields['company']??''); if ($fields['companySuffix']) { $suppEnt->companySuffix = mb_strtoupper($fields['companySuffix']); } $suppEnt->alias = mb_strtoupper($fields['alias']??''); if ($fields['category']) { $suppEnt->category = $fields['category']; } if ($fields['fiscalCode']) { $suppEnt->fiscalCode = $fields['fiscalCode']; } if ($fields['regNo']) { $suppEnt->regNo = $fields['regNo']; } if ($fields['vatRegistered']) { $suppEnt->vatRegistered = 1; if ($fields['vatCertificateNumber']) { $suppEnt->vatCertificateNumber = $fields['vatCertificateNumber']; } } $suppEnt->save(); return $suppEnt; } protected function developerMessage_submit_supplier(string $arguments, array $response) : string { if ($response['idSupplier']) { return "Supplier created with ID: ".$response['idSupplier'].". Allow the user to see the `Suppliers administration` section by giving him a clickable link to the section: " . \Engine\Mvc\Dispatcher::getInstance()->getAppUrl('Modules/Partners/Suppliers', 'index'); } return ''; } } 