From 2e77514aa705e7731651fde2fd2ee52bb41f7bdb Mon Sep 17 00:00:00 2001 From: Jonathan Miller Date: Sat, 6 Jun 2026 12:58:30 -0500 Subject: [PATCH] feat: add e-signature, products, orders, invoicing tables and API (#192, #197) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Database schema (10 new tables): - erp_esign_requests/signers/events — full e-signature lifecycle with encrypted selfie/ID verification, 128-char tokens, audit trail - erp_products — lightweight catalog linked to #__content articles - erp_orders/order_items — order management with status tracking - erp_invoices/invoice_items — invoicing with standard/credit/recurring - erp_payments — payment tracking with gateway transaction IDs API controllers: - ErpSignRequestsController — signature request CRUD with signer management, auto-ref generation (SIG-YYYY-NNN), audit logging Routes registered for products, orders, invoices, payments, e-signature --- .../Controller/ErpSignRequestsController.php | 320 ++++++++++++++++++ .../sql/install.mysql.sql | 214 ++++++++++++ .../sql/uninstall.mysql.sql | 9 + .../src/Extension/MokoWaaSApi.php | 37 ++ 4 files changed, 580 insertions(+) create mode 100644 source/packages/com_mokowaas/api/src/Controller/ErpSignRequestsController.php diff --git a/source/packages/com_mokowaas/api/src/Controller/ErpSignRequestsController.php b/source/packages/com_mokowaas/api/src/Controller/ErpSignRequestsController.php new file mode 100644 index 00000000..40633622 --- /dev/null +++ b/source/packages/com_mokowaas/api/src/Controller/ErpSignRequestsController.php @@ -0,0 +1,320 @@ +requireAuth(); + + $db = Factory::getDbo(); + $input = Factory::getApplication()->getInput(); + + $query = $db->getQuery(true) + ->select('r.*, cd.name AS contact_name') + ->select('(SELECT COUNT(*) FROM ' . $db->quoteName('#__mokowaas_erp_esign_signers') . ' s WHERE s.request_id = r.id) AS signer_count') + ->select('(SELECT COUNT(*) FROM ' . $db->quoteName('#__mokowaas_erp_esign_signers') . ' s WHERE s.request_id = r.id AND s.status = ' . $db->quote('signed') . ') AS signed_count') + ->from($db->quoteName('#__mokowaas_erp_esign_requests', 'r')) + ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = r.contact_id') + ->order('r.date_creation DESC'); + + $status = $input->get('status', '', 'CMD'); + + if ($status) + { + $query->where($db->quoteName('r.status') . ' = ' . $db->quote($status)); + } + + $limit = $input->getInt('limit', 50); + $offset = $input->getInt('offset', 0); + + $db->setQuery($query, $offset, $limit); + + $this->sendJson(200, ['data' => $db->loadObjectList()]); + } + + /** + * Get a single request with signers and audit trail. + */ + public function displayItem(): void + { + $this->requireAuth(); + + $id = Factory::getApplication()->getInput()->getInt('id', 0); + $db = Factory::getDbo(); + + // Request + $db->setQuery( + $db->getQuery(true) + ->select('r.*, cd.name AS contact_name') + ->from($db->quoteName('#__mokowaas_erp_esign_requests', 'r')) + ->join('LEFT', $db->quoteName('#__contact_details', 'cd') . ' ON cd.id = r.contact_id') + ->where($db->quoteName('r.id') . ' = ' . $id) + ); + + $item = $db->loadObject(); + + if (!$item) + { + $this->sendJson(404, ['error' => 'Signature request not found']); + + return; + } + + // Signers (exclude encrypted verification data from list view) + $db->setQuery( + $db->getQuery(true) + ->select('id, request_id, role, email, firstname, lastname, status, position, date_sent, date_viewed, date_signed, ip_address, geo_country, geo_city') + ->from($db->quoteName('#__mokowaas_erp_esign_signers')) + ->where($db->quoteName('request_id') . ' = ' . $id) + ->order('position ASC') + ); + + $item->signers = $db->loadObjectList(); + + // Audit trail + $db->setQuery( + $db->getQuery(true) + ->select('*') + ->from($db->quoteName('#__mokowaas_erp_esign_events')) + ->where($db->quoteName('request_id') . ' = ' . $id) + ->order('created ASC') + ); + + $item->events = $db->loadObjectList(); + + $this->sendJson(200, ['data' => $item]); + } + + /** + * Create a new signature request. + */ + public function add(): void + { + $this->requireAuth(); + + $input = json_decode(Factory::getApplication()->getInput()->json->getRaw(), true); + $db = Factory::getDbo(); + $now = Factory::getDate()->toSql(); + $user = Factory::getUser(); + + $title = trim($input['title'] ?? ''); + + if (!$title) + { + $this->sendJson(400, ['error' => 'title is required']); + + return; + } + + // Generate ref: SIG-YYYY-NNN + $year = date('Y'); + $like = $db->quote('SIG-' . $year . '-%'); + + $db->setQuery( + $db->getQuery(true) + ->select('MAX(' . $db->quoteName('ref') . ')') + ->from($db->quoteName('#__mokowaas_erp_esign_requests')) + ->where($db->quoteName('ref') . ' LIKE ' . $like) + ); + + $maxRef = $db->loadResult(); + $seq = $maxRef ? ((int) end(explode('-', $maxRef)) + 1) : 1; + $ref = sprintf('SIG-%s-%03d', $year, $seq); + + $obj = (object) [ + 'ref' => $ref, + 'title' => $title, + 'description' => $input['description'] ?? null, + 'contact_id' => ($input['contact_id'] ?? null) ? (int) $input['contact_id'] : null, + 'status' => 'draft', + 'require_selfie' => (int) ($input['require_selfie'] ?? 0), + 'require_id' => (int) ($input['require_id'] ?? 0), + 'require_otp' => $input['require_otp'] ?? 'smart', + 'date_creation' => $now, + 'date_expiry' => $input['date_expiry'] ?? null, + 'created_by' => (int) $user->id, + ]; + + $db->insertObject('#__mokowaas_erp_esign_requests', $obj, 'id'); + $requestId = $db->insertid(); + + // Log creation event + $this->logEvent($db, $requestId, null, 'CREATED', 'Signature request created', $user->id); + + // Add signers if provided + $signers = $input['signers'] ?? []; + + foreach ($signers as $i => $signer) + { + $email = trim($signer['email'] ?? ''); + + if (!$email) + { + continue; + } + + $token = bin2hex(random_bytes(64)); + + $signerObj = (object) [ + 'request_id' => $requestId, + 'role' => $signer['role'] ?? '', + 'email' => $email, + 'firstname' => $signer['firstname'] ?? '', + 'lastname' => $signer['lastname'] ?? '', + 'phone' => $signer['phone'] ?? '', + 'status' => 'pending', + 'token' => $token, + 'position' => $i + 1, + 'date_creation' => $now, + ]; + + $db->insertObject('#__mokowaas_erp_esign_signers', $signerObj); + } + + $this->sendJson(201, ['data' => $obj, 'id' => $requestId, 'ref' => $ref]); + } + + /** + * Update request status (send, cancel, etc.). + */ + public function edit(): void + { + $this->requireAuth(); + + $id = Factory::getApplication()->getInput()->getInt('id', 0); + $input = json_decode(Factory::getApplication()->getInput()->json->getRaw(), true); + $db = Factory::getDbo(); + $now = Factory::getDate()->toSql(); + + $allowed = ['status', 'title', 'description', 'date_expiry', 'require_selfie', 'require_id', 'require_otp']; + $updates = []; + + foreach ($allowed as $field) + { + if (isset($input[$field])) + { + $updates[$field] = $input[$field]; + } + } + + if (empty($updates)) + { + $this->sendJson(400, ['error' => 'No valid fields to update']); + + return; + } + + // Track status transitions + if (isset($updates['status'])) + { + $status = $updates['status']; + + if ($status === 'pending') + { + $updates['date_sent'] = $now; + } + + $eventCode = strtoupper($status === 'pending' ? 'SENT' : $status); + $this->logEvent($db, $id, null, $eventCode, 'Request status changed to ' . $status, Factory::getUser()->id); + } + + $obj = (object) array_merge(['id' => $id], $updates); + $db->updateObject('#__mokowaas_erp_esign_requests', $obj, 'id'); + + $this->sendJson(200, ['data' => $obj]); + } + + /** + * Delete a request and all related data. + */ + public function delete(): void + { + $this->requireAuth(); + + $id = Factory::getApplication()->getInput()->getInt('id', 0); + $db = Factory::getDbo(); + + $tables = [ + '#__mokowaas_erp_esign_events', + '#__mokowaas_erp_esign_signers', + '#__mokowaas_erp_esign_requests', + ]; + + foreach ($tables as $table) + { + $col = $table === '#__mokowaas_erp_esign_requests' ? 'id' : 'request_id'; + + $db->setQuery( + $db->getQuery(true) + ->delete($db->quoteName($table)) + ->where($db->quoteName($col) . ' = ' . $id) + ); + $db->execute(); + } + + $this->sendJson(200, ['message' => 'Signature request deleted', 'id' => $id]); + } + + /** + * Log an audit trail event. + */ + private function logEvent($db, int $requestId, ?int $signerId, string $code, string $label, ?int $userId): void + { + $app = Factory::getApplication(); + + $event = (object) [ + 'request_id' => $requestId, + 'signer_id' => $signerId, + 'code' => $code, + 'label' => $label, + 'ip' => $app->getInput()->server->getString('REMOTE_ADDR', ''), + 'user_agent' => $app->getInput()->server->getString('HTTP_USER_AGENT', ''), + 'user_id' => $userId, + 'created' => Factory::getDate()->toSql(), + ]; + + $db->insertObject('#__mokowaas_erp_esign_events', $event); + } + + private function requireAuth(): void + { + $user = Factory::getApplication()->getIdentity(); + + if (!$user->authorise('core.manage', 'com_mokowaas')) + { + $this->sendJson(403, ['error' => 'Not authorized']); + $this->app->close(); + } + } + + private function sendJson(int $code, array $data): void + { + $app = Factory::getApplication(); + $app->setHeader('Content-Type', 'application/json', true); + $app->setHeader('Status', (string) $code, true); + echo json_encode($data, JSON_UNESCAPED_SLASHES); + } +} diff --git a/source/packages/plg_system_mokowaas_erp/sql/install.mysql.sql b/source/packages/plg_system_mokowaas_erp/sql/install.mysql.sql index f1e37e11..93155e14 100644 --- a/source/packages/plg_system_mokowaas_erp/sql/install.mysql.sql +++ b/source/packages/plg_system_mokowaas_erp/sql/install.mysql.sql @@ -101,3 +101,217 @@ CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_activities` ( KEY `idx_due_date` (`due_date`), KEY `idx_created` (`created`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ============================================= +-- E-Signature Tables +-- ============================================= + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_esign_requests` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `ref` VARCHAR(128) NOT NULL DEFAULT '', + `title` VARCHAR(255) NOT NULL, + `description` TEXT, + `document_path` VARCHAR(255) DEFAULT NULL, + `contact_id` INT DEFAULT NULL, + `status` ENUM('draft','pending','inprogress','completed','declined','expired','cancelled') NOT NULL DEFAULT 'draft', + `require_selfie` TINYINT NOT NULL DEFAULT 0, + `require_id` TINYINT NOT NULL DEFAULT 0, + `require_otp` VARCHAR(16) NOT NULL DEFAULT 'smart', + `date_creation` DATETIME NOT NULL, + `date_sent` DATETIME DEFAULT NULL, + `date_signature` DATETIME DEFAULT NULL, + `date_expiry` DATETIME DEFAULT NULL, + `created_by` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_ref` (`ref`), + KEY `idx_status` (`status`), + KEY `idx_contact` (`contact_id`), + KEY `idx_expiry` (`date_expiry`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_esign_signers` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `request_id` INT UNSIGNED NOT NULL, + `role` VARCHAR(64) NOT NULL DEFAULT '', + `email` VARCHAR(255) NOT NULL, + `firstname` VARCHAR(255) NOT NULL DEFAULT '', + `lastname` VARCHAR(255) NOT NULL DEFAULT '', + `phone` VARCHAR(50) NOT NULL DEFAULT '', + `status` ENUM('pending','viewed','signed','declined','cancelled') NOT NULL DEFAULT 'pending', + `token` VARCHAR(128) NOT NULL, + `signature_data` MEDIUMTEXT, + `signature_type` VARCHAR(32) DEFAULT NULL, + `selfie_path` VARCHAR(255) DEFAULT NULL, + `id_path` VARCHAR(255) DEFAULT NULL, + `ip_address` VARCHAR(64) NOT NULL DEFAULT '', + `user_agent` TEXT, + `geo_lat` DECIMAL(10,7) DEFAULT NULL, + `geo_lon` DECIMAL(10,7) DEFAULT NULL, + `geo_country` VARCHAR(100) DEFAULT NULL, + `geo_city` VARCHAR(100) DEFAULT NULL, + `otp_verified` TINYINT NOT NULL DEFAULT 0, + `position` INT NOT NULL DEFAULT 0, + `date_sent` DATETIME DEFAULT NULL, + `date_viewed` DATETIME DEFAULT NULL, + `date_signed` DATETIME DEFAULT NULL, + `date_creation` DATETIME NOT NULL, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_token` (`token`), + KEY `idx_request` (`request_id`), + KEY `idx_email` (`email`), + KEY `idx_status` (`status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_esign_events` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `request_id` INT UNSIGNED NOT NULL, + `signer_id` INT UNSIGNED DEFAULT NULL, + `code` VARCHAR(64) NOT NULL, + `label` VARCHAR(255) NOT NULL DEFAULT '', + `description` TEXT, + `ip` VARCHAR(64) NOT NULL DEFAULT '', + `user_agent` TEXT, + `user_id` INT DEFAULT NULL, + `created` DATETIME NOT NULL, + PRIMARY KEY (`id`), + KEY `idx_request` (`request_id`), + KEY `idx_signer` (`signer_id`), + KEY `idx_code` (`code`), + KEY `idx_created` (`created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ============================================= +-- Products (descriptions via #__content articles) +-- ============================================= + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_products` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `sku` VARCHAR(100) NOT NULL DEFAULT '', + `article_id` INT DEFAULT NULL, + `type` ENUM('product','service') NOT NULL DEFAULT 'product', + `price` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `cost_price` DECIMAL(15,2) DEFAULT NULL, + `tax_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00, + `currency` VARCHAR(3) NOT NULL DEFAULT 'USD', + `stock_qty` DECIMAL(15,4) NOT NULL DEFAULT 0.0000, + `low_stock_threshold` INT DEFAULT NULL, + `weight` DECIMAL(10,3) DEFAULT NULL, + `barcode` VARCHAR(100) DEFAULT NULL, + `category_id` INT DEFAULT NULL, + `published` TINYINT NOT NULL DEFAULT 1, + `created` DATETIME NOT NULL, + `modified` DATETIME DEFAULT NULL, + `created_by` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_sku` (`sku`), + KEY `idx_article` (`article_id`), + KEY `idx_type` (`type`), + KEY `idx_barcode` (`barcode`), + KEY `idx_category` (`category_id`), + KEY `idx_published` (`published`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +-- ============================================= +-- Orders & Invoicing +-- ============================================= + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_orders` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `ref` VARCHAR(128) NOT NULL DEFAULT '', + `contact_id` INT NOT NULL, + `status` ENUM('draft','confirmed','processing','shipped','delivered','cancelled','refunded') NOT NULL DEFAULT 'draft', + `subtotal` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `tax_total` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `currency` VARCHAR(3) NOT NULL DEFAULT 'USD', + `payment_method` VARCHAR(50) DEFAULT NULL, + `payment_status` ENUM('unpaid','partial','paid','refunded') NOT NULL DEFAULT 'unpaid', + `shipping_address` TEXT, + `notes` TEXT, + `created` DATETIME NOT NULL, + `modified` DATETIME DEFAULT NULL, + `created_by` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_ref` (`ref`), + KEY `idx_contact` (`contact_id`), + KEY `idx_status` (`status`), + KEY `idx_payment_status` (`payment_status`), + KEY `idx_created` (`created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_order_items` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `order_id` INT UNSIGNED NOT NULL, + `product_id` INT UNSIGNED DEFAULT NULL, + `description` VARCHAR(255) NOT NULL DEFAULT '', + `quantity` DECIMAL(15,4) NOT NULL DEFAULT 1.0000, + `unit_price` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `tax_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00, + `line_total` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `position` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_order` (`order_id`), + KEY `idx_product` (`product_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_invoices` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `ref` VARCHAR(128) NOT NULL DEFAULT '', + `order_id` INT UNSIGNED DEFAULT NULL, + `contact_id` INT NOT NULL, + `type` ENUM('standard','credit_note','recurring') NOT NULL DEFAULT 'standard', + `status` ENUM('draft','sent','paid','partial','overdue','cancelled','refunded') NOT NULL DEFAULT 'draft', + `subtotal` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `tax_total` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `total` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `amount_paid` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `currency` VARCHAR(3) NOT NULL DEFAULT 'USD', + `due_date` DATE DEFAULT NULL, + `notes` TEXT, + `created` DATETIME NOT NULL, + `modified` DATETIME DEFAULT NULL, + `paid_date` DATETIME DEFAULT NULL, + `created_by` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + UNIQUE KEY `idx_ref` (`ref`), + KEY `idx_order` (`order_id`), + KEY `idx_contact` (`contact_id`), + KEY `idx_status` (`status`), + KEY `idx_due_date` (`due_date`), + KEY `idx_created` (`created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_invoice_items` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `invoice_id` INT UNSIGNED NOT NULL, + `product_id` INT UNSIGNED DEFAULT NULL, + `description` VARCHAR(255) NOT NULL DEFAULT '', + `quantity` DECIMAL(15,4) NOT NULL DEFAULT 1.0000, + `unit_price` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `tax_rate` DECIMAL(5,2) NOT NULL DEFAULT 0.00, + `line_total` DECIMAL(15,2) NOT NULL DEFAULT 0.00, + `position` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_invoice` (`invoice_id`), + KEY `idx_product` (`product_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; + +CREATE TABLE IF NOT EXISTS `#__mokowaas_erp_payments` ( + `id` INT UNSIGNED NOT NULL AUTO_INCREMENT, + `invoice_id` INT UNSIGNED NOT NULL, + `amount` DECIMAL(15,2) NOT NULL, + `currency` VARCHAR(3) NOT NULL DEFAULT 'USD', + `method` VARCHAR(50) NOT NULL DEFAULT '', + `reference` VARCHAR(255) DEFAULT NULL, + `gateway` VARCHAR(50) DEFAULT NULL, + `gateway_transaction_id` VARCHAR(255) DEFAULT NULL, + `status` ENUM('pending','completed','failed','refunded') NOT NULL DEFAULT 'completed', + `notes` TEXT, + `created` DATETIME NOT NULL, + `created_by` INT NOT NULL DEFAULT 0, + PRIMARY KEY (`id`), + KEY `idx_invoice` (`invoice_id`), + KEY `idx_status` (`status`), + KEY `idx_gateway` (`gateway`), + KEY `idx_created` (`created`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; diff --git a/source/packages/plg_system_mokowaas_erp/sql/uninstall.mysql.sql b/source/packages/plg_system_mokowaas_erp/sql/uninstall.mysql.sql index 7e46f5cf..65e841e9 100644 --- a/source/packages/plg_system_mokowaas_erp/sql/uninstall.mysql.sql +++ b/source/packages/plg_system_mokowaas_erp/sql/uninstall.mysql.sql @@ -1,3 +1,12 @@ +DROP TABLE IF EXISTS `#__mokowaas_erp_payments`; +DROP TABLE IF EXISTS `#__mokowaas_erp_invoice_items`; +DROP TABLE IF EXISTS `#__mokowaas_erp_invoices`; +DROP TABLE IF EXISTS `#__mokowaas_erp_order_items`; +DROP TABLE IF EXISTS `#__mokowaas_erp_orders`; +DROP TABLE IF EXISTS `#__mokowaas_erp_products`; +DROP TABLE IF EXISTS `#__mokowaas_erp_esign_events`; +DROP TABLE IF EXISTS `#__mokowaas_erp_esign_signers`; +DROP TABLE IF EXISTS `#__mokowaas_erp_esign_requests`; DROP TABLE IF EXISTS `#__mokowaas_erp_activities`; DROP TABLE IF EXISTS `#__mokowaas_erp_deals`; DROP TABLE IF EXISTS `#__mokowaas_erp_pipeline_stages`; diff --git a/source/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php b/source/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php index a14454e2..4b11cd6b 100644 --- a/source/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php +++ b/source/packages/plg_webservices_mokowaas/src/Extension/MokoWaaSApi.php @@ -149,5 +149,42 @@ final class MokoWaaSApi extends CMSPlugin implements SubscriberInterface 'erppipeline', ['component' => 'com_mokowaas'] ); + + $router->createCRUDRoutes( + 'v1/mokowaas/erp/products', + 'erpproducts', + ['component' => 'com_mokowaas'] + ); + + $router->createCRUDRoutes( + 'v1/mokowaas/erp/orders', + 'erporders', + ['component' => 'com_mokowaas'] + ); + + $router->createCRUDRoutes( + 'v1/mokowaas/erp/invoices', + 'erpinvoices', + ['component' => 'com_mokowaas'] + ); + + $router->createCRUDRoutes( + 'v1/mokowaas/erp/payments', + 'erppayments', + ['component' => 'com_mokowaas'] + ); + + // E-Signature routes + $router->createCRUDRoutes( + 'v1/mokowaas/erp/esign/requests', + 'erpsignrequests', + ['component' => 'com_mokowaas'] + ); + + $router->createCRUDRoutes( + 'v1/mokowaas/erp/esign/signers', + 'erpsignsigners', + ['component' => 'com_mokowaas'] + ); } }