ultimatepos/docs/generate-access-doc.php

389 lines
19 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
/**
* Generate Persian access guide Word document (.docx)
* Run: php docs/generate-access-doc.php
*/
require __DIR__ . '/../vendor/autoload.php';
$app = require_once __DIR__ . '/../bootstrap/app.php';
$app->make(\Illuminate\Contracts\Console\Kernel::class)->bootstrap();
$outputFile = sys_get_temp_dir() . DIRECTORY_SEPARATOR . 'dastresi-pos-davari-company.docx';
$siteUrl = 'https://pos.davari.company';
$knownPasswords = ['123456', 'password', 'admin', 'admin123'];
$userAccounts = [];
$businesses = \App\Business::pluck('name', 'id');
foreach (\App\User::whereNull('deleted_at')->orderBy('business_id')->orderBy('id')->get() as $user) {
$password = '—';
foreach ($knownPasswords as $candidate) {
if (\Illuminate\Support\Facades\Hash::check($candidate, $user->password)) {
$password = $candidate;
break;
}
}
$roles = $user->getRoleNames()->map(function ($role) use ($user) {
return str_replace('#' . $user->business_id, '', $role);
})->implode('، ');
$userAccounts[] = [
$user->username,
trim(($user->first_name ?? '') . ' ' . ($user->last_name ?? '')),
$roles ?: '—',
$businesses[$user->business_id] ?? '—',
$user->email,
$password,
];
}
$sections = [
'اطلاعات کلی' => [
'نام سیستم' => 'Ultimate POS - مدیریت انبار و فروشگاه',
'آدرس سایت' => $siteUrl,
'صفحه ورود' => $siteUrl . '/login',
'صفحه فروش (POS)' => $siteUrl . '/pos/create',
'داشبورد' => $siteUrl . '/home',
'نسخه' => '6.9',
],
'نحوه ورود به سیستم' => [
'۱' => 'مرورگر خود را باز کنید و به آدرس ' . $siteUrl . ' بروید.',
'۲' => 'روی «ورود» کلیک کنید یا مستقیماً به ' . $siteUrl . '/login بروید.',
'۳' => 'نام کاربری (username) و رمز عبور را وارد کنید. ورود با ایمیل انجام نمی‌شود.',
'۴' => 'پس از ورود، به داشبورد اصلی سیستم هدایت می‌شوید.',
'حساب اصلی' => 'نام کاربری: admin | رمز عبور: 123456',
'حساب صندوقدار' => 'نام کاربری: cashier | رمز عبور: 123456',
'توجه' => 'برای بازیابی رمز از گزینه «فراموشی رمز عبور» استفاده کنید. پس از استقرار نهایی، رمزها را تغییر دهید.',
],
'نقش‌های پیش‌فرض سیستم' => [
'مدیر (Admin)' => 'دسترسی کامل به تمام بخش‌های سیستم. این نقش به‌صورت خودکار هنگام ایجاد کسب‌وکار ساخته می‌شود و تمام مجوزها را دارد.',
'صندوقدار (Cashier)' => 'دسترسی محدود به فروش POS، مشاهده/ایجاد/ویرایش/حذف فروش، دسترسی به تمام مکان‌ها، مشاهده و بستن صندوق.',
'نقش‌های سفارشی' => 'مدیر می‌تواند از منوی تنظیمات > نقش‌ها و مجوزها، نقش‌های جدید با مجوزهای دلخواه ایجاد کند.',
],
'مدیریت نقش‌ها و مجوزها' => [
'مسیر' => 'تنظیمات > نقش‌ها و مجوزها (Roles)',
'افزودن نقش' => 'تنظیمات > نقش‌ها > افزودن نقش',
'ویرایش نقش' => 'از لیست نقش‌ها، دکمه ویرایش را بزنید.',
'تخصیص به کاربر' => 'تنظیمات > مدیریت کاربران > افزودن/ویرایش کاربر > انتخاب نقش',
'دسترسی مکان' => 'هر کاربر می‌تواند به یک یا چند مکان (شعبه) دسترسی داشته باشد یا با مجوز «دسترسی به تمام مکان‌ها» به همه شعب دسترسی پیدا کند.',
],
];
$permissionGroups = [
'کاربران' => [
'user.view' => 'مشاهده کاربران',
'user.create' => 'افزودن کاربر',
'user.update' => 'ویرایش کاربر',
'user.delete' => 'حذف کاربر',
'roles.view' => 'مشاهده نقش‌ها',
'roles.create' => 'افزودن نقش',
'roles.update' => 'ویرایش نقش',
'roles.delete' => 'حذف نقش',
],
'تأمین‌کنندگان' => [
'supplier.view' => 'مشاهده تأمین‌کنندگان',
'supplier.create' => 'افزودن تأمین‌کننده',
'supplier.update' => 'ویرایش تأمین‌کننده',
'supplier.delete' => 'حذف تأمین‌کننده',
],
'مشتریان' => [
'customer.view' => 'مشاهده مشتریان',
'customer.create' => 'افزودن مشتری',
'customer.update' => 'ویرایش مشتری',
'customer.delete' => 'حذف مشتری',
],
'محصولات' => [
'product.view' => 'مشاهده محصولات',
'product.create' => 'افزودن محصول',
'product.update' => 'ویرایش محصول',
'product.delete' => 'حذف محصول',
'product.opening_stock' => 'افزودن موجودی اولیه',
'view_purchase_price' => 'مشاهده قیمت خرید',
'access_default_selling_price' => 'دسترسی به قیمت فروش پیش‌فرض',
],
'خرید' => [
'purchase.view' => 'مشاهده خرید',
'purchase.view_own' => 'مشاهده خریدهای خود',
'purchase.view_all' => 'مشاهده تمام خریدها',
'purchase.create' => 'افزودن خرید',
'purchase.update' => 'ویرایش خرید',
'purchase.delete' => 'حذف خرید',
'purchase.payments' => 'پرداخت خرید',
'purchase.update_status' => 'تغییر وضعیت خرید',
],
'فروش و POS' => [
'sell.view' => 'مشاهده فروش POS',
'sell.create' => 'افزودن فروش POS',
'sell.update' => 'ویرایش فروش POS',
'sell.delete' => 'حذف فروش POS',
'sell.payments' => 'پرداخت فروش',
'direct_sell.access' => 'دسترسی به فروش مستقیم',
'view_own_sell_only' => 'مشاهده فقط فروش‌های خود',
'list_drafts' => 'لیست پیش‌نویس‌ها',
'list_quotations' => 'لیست پیش‌فاکتورها',
'print_invoice' => 'چاپ فاکتور',
'discount.access' => 'دسترسی به تخفیف',
'edit_product_price_from_pos_screen' => 'ویرایش قیمت از صفحه POS',
'edit_product_discount_from_pos_screen' => 'ویرایش تخفیف از صفحه POS',
'edit_product_price_from_sale_screen' => 'ویرایش قیمت از صفحه فروش',
'edit_product_discount_from_sale_screen' => 'ویرایش تخفیف از صفحه فروش',
],
'موجودی' => [
'stock_adjustment.view' => 'مشاهده تمام تعدیلات موجودی',
'stock_adjustment.view_own' => 'مشاهده تعدیلات خود',
'stock_adjustment.create' => 'افزودن تعدیل موجودی',
'stock_adjustment.update' => 'ویرایش تعدیل موجودی',
'stock_adjustment.delete' => 'حذف تعدیل موجودی',
'stock_transfer.view' => 'مشاهده تمام انتقالات',
'stock_transfer.view_own' => 'مشاهده انتقالات خود',
'stock_transfer.create' => 'افزودن انتقال موجودی',
'stock_transfer.update' => 'ویرایش انتقال موجودی',
'stock_transfer.delete' => 'حذف انتقال موجودی',
],
'گزارش‌ها' => [
'purchase_n_sell_report.view' => 'گزارش خرید و فروش',
'contacts_report.view' => 'گزارش تأمین‌کنندگان و مشتریان',
'stock_report.view' => 'گزارش موجودی و انقضا',
'tax_report.view' => 'گزارش مالیات',
'trending_product_report.view' => 'گزارش محصولات پرفروش',
'register_report.view' => 'گزارش صندوق',
'sales_representative.view' => 'گزارش نمایندگان فروش',
'expense_report.view' => 'گزارش هزینه‌ها',
'profit_loss_report.view' => 'گزارش سود و زیان',
],
'تنظیمات' => [
'business_settings.access' => 'تنظیمات کسب‌وکار',
'barcode_settings.access' => 'تنظیمات بارکد',
'invoice_settings.access' => 'تنظیمات فاکتور',
'dashboard.data' => 'مشاهده داده‌های داشبورد',
'access_all_locations' => 'دسترسی به تمام مکان‌ها',
'access_shipping' => 'دسترسی به ارسال',
'account.access' => 'دسترسی به حساب‌ها',
'expense.access' => 'دسترسی به هزینه‌ها',
],
'برند، مالیات، واحد، دسته‌بندی' => [
'brand.view/create/update/delete' => 'مدیریت برندها',
'tax_rate.view/create/update/delete' => 'مدیریت نرخ مالیات',
'unit.view/create/update/delete' => 'مدیریت واحدها',
'category.view/create/update/delete' => 'مدیریت دسته‌بندی‌ها',
],
'صندوق' => [
'view_cash_register' => 'مشاهده صندوق',
'close_cash_register' => 'بستن صندوق',
],
'رزرو (رستوران)' => [
'crud_all_bookings' => 'مدیریت تمام رزروها',
'crud_own_bookings' => 'مدیریت رزروهای خود',
],
];
$modulePermissions = [
'Essentials (منابع انسانی)' => [
'essentials.create_message' => 'ایجاد پیام',
'essentials.view_message' => 'مشاهده پیام',
'essentials.approve_leave' => 'تأیید مرخصی',
'essentials.assign_todos' => 'تخصیص کارها',
'essentials.add_allowance_and_deduction' => 'افزودن کمک‌هزینه و کسر',
],
'Repair (تعمیرات)' => [
'repair.view/create/update/delete' => 'مدیریت تعمیرات',
'repair_status.access/update' => 'مدیریت وضعیت تعمیر',
],
'Manufacturing (تولید)' => [
'manufacturing.*' => 'دسترسی به ماژول تولید و فرمول‌ها',
],
'Project (پروژه)' => [
'project.create_project' => 'ایجاد پروژه',
'project.edit_project' => 'ویرایش پروژه',
'project.delete_project' => 'حذف پروژه',
],
'Woocommerce' => [
'woocommerce.sync_products' => 'همگام‌سازی محصولات',
'woocommerce.sync_orders' => 'همگام‌سازی سفارشات',
'woocommerce.syc_categories' => 'همگام‌سازی دسته‌بندی‌ها',
'woocommerce.map_tax_rates' => 'نگاشت نرخ مالیات',
'woocommerce.access_woocommerce_api_settings' => 'تنظیمات API ووکامرس',
],
'CRM' => [
'crm.*' => 'دسترسی به ماژول CRM و پیگیری مشتریان',
],
'Accounting (حسابداری)' => [
'accounting.*' => 'دسترسی به ماژول حسابداری',
],
];
$apiEndpoints = [
'پایه API' => $siteUrl . '/connector/api/',
'احراز هویت' => 'OAuth2 / Passport Token (از تنظیمات Connector > Client API)',
'محصولات' => 'GET /connector/api/product',
'فروش' => 'GET/POST /connector/api/sell',
'مشتریان' => 'GET/POST /connector/api/contactapi',
'کاربر فعلی' => 'GET /connector/api/user/loggedin',
'مکان‌های کسب‌وکار' => 'GET /connector/api/business-location',
'صندوق' => 'GET/POST /connector/api/cash-register',
'گزارش سود و زیان' => 'GET /connector/api/profit-loss-report',
'گزارش موجودی' => 'GET /connector/api/product-stock-report',
'CRM' => $siteUrl . '/connector/api/crm/',
'Field Force' => $siteUrl . '/connector/api/field-force',
];
function xmlEscape(string $text): string
{
return htmlspecialchars($text, ENT_XML1 | ENT_QUOTES, 'UTF-8');
}
function makeParagraph(string $text, bool $bold = false, int $size = 24): string
{
$boldTag = $bold ? '<w:b/>' : '';
return '<w:p>
<w:pPr><w:jc w:val="right"/><w:bidi/></w:pPr>
<w:r>
<w:rPr>' . $boldTag . '<w:sz w:val="' . $size . '"/><w:szCs w:val="' . $size . '"/><w:rtl/></w:rPr>
<w:t xml:space="preserve">' . xmlEscape($text) . '</w:t>
</w:r>
</w:p>';
}
function makeHeading(string $text, int $level = 1): string
{
$sizes = [1 => 36, 2 => 30, 3 => 26];
$size = $sizes[$level] ?? 26;
return '<w:p>
<w:pPr><w:jc w:val="right"/><w:bidi/></w:pPr>
<w:r>
<w:rPr><w:b/><w:sz w:val="' . $size . '"/><w:szCs w:val="' . $size . '"/><w:rtl/></w:rPr>
<w:t xml:space="preserve">' . xmlEscape($text) . '</w:t>
</w:r>
</w:p>';
}
function makeTableRow(array $cells, bool $header = false): string
{
$row = '<w:tr>';
foreach ($cells as $cell) {
$bold = $header ? '<w:b/>' : '';
$row .= '<w:tc>
<w:tcPr><w:tcW w:w="4500" w:type="dxa"/></w:tcPr>
<w:p>
<w:pPr><w:jc w:val="right"/><w:bidi/></w:pPr>
<w:r>
<w:rPr>' . $bold . '<w:sz w:val="22"/><w:szCs w:val="22"/><w:rtl/></w:rPr>
<w:t xml:space="preserve">' . xmlEscape($cell) . '</w:t>
</w:r>
</w:p>
</w:tc>';
}
$row .= '</w:tr>';
return $row;
}
$body = '';
$body .= makeHeading('راهنمای دسترسی سیستم Ultimate POS', 1);
$body .= makeParagraph('شرکت داواری', false, 26);
$body .= makeParagraph('تاریخ تهیه: ' . date('Y/m/d'), false, 22);
$body .= makeParagraph('');
$body .= makeHeading('اطلاعات ورود (نام کاربری و رمز عبور)', 2);
$body .= makeParagraph('آدرس ورود: ' . $siteUrl . '/login', true, 24);
$body .= makeParagraph('ورود با فیلد «نام کاربری» انجام می‌شود (نه ایمیل).');
$body .= '<w:tbl><w:tblPr><w:bidiVisual/><w:tblW w:w="0" w:type="auto"/></w:tblPr>';
$body .= makeTableRow(['نام کاربری', 'نام', 'نقش', 'کسب‌وکار', 'ایمیل', 'رمز عبور'], true);
foreach ($userAccounts as $account) {
$body .= makeTableRow($account);
}
$body .= '</w:tbl>';
$body .= makeParagraph('');
foreach ($sections as $title => $items) {
$body .= makeHeading($title, 2);
$body .= '<w:tbl><w:tblPr><w:bidiVisual/><w:tblW w:w="0" w:type="auto"/></w:tblPr>';
$body .= makeTableRow(['عنوان', 'توضیحات'], true);
foreach ($items as $key => $value) {
$body .= makeTableRow([$key, $value]);
}
$body .= '</w:tbl>';
$body .= makeParagraph('');
}
$body .= makeHeading('لیست مجوزهای اصلی سیستم', 2);
foreach ($permissionGroups as $group => $perms) {
$body .= makeHeading($group, 3);
$body .= '<w:tbl><w:tblPr><w:bidiVisual/><w:tblW w:w="0" w:type="auto"/></w:tblPr>';
$body .= makeTableRow(['کد مجوز', 'توضیح فارسی'], true);
foreach ($perms as $code => $desc) {
$body .= makeTableRow([$code, $desc]);
}
$body .= '</w:tbl>';
$body .= makeParagraph('');
}
$body .= makeHeading('مجوزهای ماژول‌های فعال', 2);
foreach ($modulePermissions as $module => $perms) {
$body .= makeHeading($module, 3);
$body .= '<w:tbl><w:tblPr><w:bidiVisual/><w:tblW w:w="0" w:type="auto"/></w:tblPr>';
$body .= makeTableRow(['کد مجوز', 'توضیح'], true);
foreach ($perms as $code => $desc) {
$body .= makeTableRow([$code, $desc]);
}
$body .= '</w:tbl>';
$body .= makeParagraph('');
}
$body .= makeHeading('دسترسی API (ماژول Connector)', 2);
$body .= makeParagraph('برای اتصال اپلیکیشن موبایل یا سیستم‌های خارجی از API زیر استفاده کنید. احراز هویت با OAuth2 Token (Laravel Passport) انجام می‌شود.');
$body .= '<w:tbl><w:tblPr><w:bidiVisual/><w:tblW w:w="0" w:type="auto"/></w:tblPr>';
$body .= makeTableRow(['سرویس', 'آدرس / مسیر'], true);
foreach ($apiEndpoints as $name => $url) {
$body .= makeTableRow([$name, $url]);
}
$body .= '</w:tbl>';
$body .= makeParagraph('');
$body .= makeHeading('نکات امنیتی', 2);
$securityNotes = [
'رمز عبور قوی برای تمام کاربران تنظیم کنید.',
'فقط مجوزهای لازم را به هر نقش اختصاص دهید (اصل کمترین دسترسی).',
'نقش Admin را فقط به افراد مورد اعتماد بدهید.',
'توکن‌های API را محرمانه نگه دارید و به‌صورت دوره‌ای تمدید کنید.',
'دسترسی مکان (شعبه) را برای کاربران محدود کنید.',
];
foreach ($securityNotes as $note) {
$body .= makeParagraph('• ' . $note);
}
$documentXml = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<w:document xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main"
xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
<w:body>' . $body . '<w:sectPr><w:pgSz w:w="11906" w:h="16838"/><w:pgMar w:top="1440" w:right="1440" w:bottom="1440" w:left="1440"/></w:sectPr></w:body>
</w:document>';
$contentTypes = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
<Default Extension="xml" ContentType="application/xml"/>
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
</Types>';
$rels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
</Relationships>';
$wordRels = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"></Relationships>';
$zip = new ZipArchive();
if ($zip->open($outputFile, ZipArchive::CREATE | ZipArchive::OVERWRITE) !== true) {
fwrite(STDERR, "Cannot create docx file\n");
exit(1);
}
$zip->addFromString('[Content_Types].xml', $contentTypes);
$zip->addFromString('_rels/.rels', $rels);
$zip->addFromString('word/document.xml', $documentXml);
$zip->addFromString('word/_rels/document.xml.rels', $wordRels);
$zip->close();
echo "Created: $outputFile\n";