389 lines
19 KiB
PHP
389 lines
19 KiB
PHP
<?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";
|