Commit f4f4b150 authored by DevPilot's avatar DevPilot

docs: دليل PDF للمحاسبة والأصول الثابتة والخزينة (67 شاشة بلقطات حقيقية)

دليل عملي واحد يغطي التلات مجالات المطلوبة بس:
- المحاسبة: الإعداد، دليل الحسابات، القيود، البنوك والشيكات، الاعتمادات
  والضمانات والقروض، وكل التقارير والقوائم المالية.
- الأصول الثابتة: السجل، الإضافة، الصيانة، العهدة، الموردين.
- الخزينة والكاشير: الورديات، التحصيل، التسويات، العهدة، الإيداعات البنكية.

كل شاشة ليها لقطة حقيقية من النظام الحي + شرح إيه اللي بتعمله وإزاي
تستخدمها والقواعد المهمة فيها.

أدوات التوليد اتحفظت تحت tools/pdf عشان الدليل يتبني تاني بعد أي تعديل.
اللقطات بتتفحص إنها مش صفحة تسجيل دخول قبل ما تتقبل — أول محاولة طلعت
65 لقطة من 67 صفحة لوجن من غير ما السكريبت ياخد باله.
parent 4197b68a
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""دليل المحاسبة والأصول والخزينة — لقطات حقيقية من النظام مع شرح الاستخدام."""
import json, os
import arabic_reshaper
from bidi.algorithm import get_display
from PIL import Image
Image.MAX_IMAGE_PIXELS = None
from reportlab.lib.pagesizes import A4
from reportlab.lib.units import cm
from reportlab.lib import colors
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_RIGHT, TA_CENTER
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.platypus import (SimpleDocTemplate, Paragraph, Spacer, Table, TableStyle,
PageBreak, HRFlowable, Image as RLImage, KeepTogether)
F = "/tmp/fin_fonts"
pdfmetrics.registerFont(TTFont("AR", f"{F}/Amiri-Regular.ttf"))
pdfmetrics.registerFont(TTFont("AR-B", f"{F}/Amiri-Bold.ttf"))
pdfmetrics.registerFont(TTFont("AR-S", f"{F}/Amiri-Bold.ttf"))
resh = arabic_reshaper.ArabicReshaper()
def ar(t):
"""سطر واحد: تشكيل ثم عكس للعرض."""
return get_display(resh.reshape(t or ""))
def ar_wrap(t, style, width):
"""
النص العربي الطويل لازم يتقسّم لسطور *قبل* ما يتعكس للعرض.
لو عكسنا النص كله الأول وسبنا reportlab يقسّمه، السطور بتطلع
بترتيب مقلوب — آخر سطر فوق وأول سطر تحت.
"""
from reportlab.pdfbase.pdfmetrics import stringWidth
shaped = resh.reshape(t or "")
words = shaped.split(' ')
lines, cur = [], ''
for w in words:
trial = (cur + ' ' + w).strip()
if stringWidth(trial, style.fontName, style.fontSize) <= width or not cur:
cur = trial
else:
lines.append(cur); cur = w
if cur:
lines.append(cur)
return '<br/>'.join(get_display(l) for l in lines)
DARK = colors.HexColor("#111827")
GRAY = colors.HexColor("#6B7280")
LINE = colors.HexColor("#E5E7EB")
S = {
"title": ParagraphStyle("t", fontName="AR-B", fontSize=30, leading=46, textColor=DARK, alignment=TA_CENTER, spaceAfter=10),
"sub": ParagraphStyle("s", fontName="AR", fontSize=14, leading=26, textColor=colors.HexColor("#0D7377"), alignment=TA_CENTER, spaceAfter=6),
"meta": ParagraphStyle("m", fontName="AR", fontSize=10, leading=19, textColor=GRAY, alignment=TA_CENTER),
"band": ParagraphStyle("b", fontName="AR-B", fontSize=18, leading=32, textColor=colors.white, alignment=TA_RIGHT),
"h2": ParagraphStyle("h2", fontName="AR-B", fontSize=13.5, leading=26, textColor=DARK, alignment=TA_RIGHT, spaceAfter=2),
"cap": ParagraphStyle("c", fontName="AR", fontSize=10.5, leading=20, textColor=colors.HexColor("#374151"), alignment=TA_RIGHT, spaceAfter=7),
"path": ParagraphStyle("p", fontName="Helvetica", fontSize=8.5, leading=14, textColor=GRAY, alignment=TA_RIGHT, spaceAfter=6),
"toc_h": ParagraphStyle("th", fontName="AR-B", fontSize=13, leading=28, textColor=DARK, alignment=TA_RIGHT, spaceBefore=10),
"toc": ParagraphStyle("tc", fontName="AR", fontSize=10.5, leading=20, textColor=colors.HexColor("#374151"), alignment=TA_RIGHT, rightIndent=14),
}
MARGIN = 2.0 * cm
W = A4[0] - 2 * MARGIN # عرض الإطار الحقيقي — لازم يطابق هوامش الصفحة بالظبط
def P(t, st="cap"):
style = S[st]
# الأنماط اللي بتتلف على أكتر من سطر لازم تتقسّم قبل العكس
if st in ("cap", "intro", "toc", "step"):
# إطار reportlab بيضيف 6pt حشو من كل ناحية افتراضياً — لازم نخصمها،
# وإلا بيلفّ كلمة زيادة وتظهر لوحدها في سطر تاني بعد العكس
avail = W - style.rightIndent - style.leftIndent - 12 - 4
return Paragraph(ar_wrap(t, style, avail), style)
return Paragraph(ar(t), style)
def band(title, hexcolor):
t = Table([[P(title, "band")]], colWidths=[W])
t.setStyle(TableStyle([
("BACKGROUND", (0,0), (-1,-1), colors.HexColor(hexcolor)),
("TOPPADDING", (0,0), (-1,-1), 12), ("BOTTOMPADDING", (0,0), (-1,-1), 12),
("LEFTPADDING", (0,0), (-1,-1), 16), ("RIGHTPADDING", (0,0), (-1,-1), 16)]))
return t
def shot(path, maxh=21.0*cm):
im = Image.open(path).convert('RGB')
w, h = im.size
# اقصص الطول الزائد عشان الصورة تفضل مقروءة
max_ratio = 1.28 # أطول من كده الصورة بتتصغّر وتبقى مش مقروءة
if h / w > max_ratio:
im = im.crop((0, 0, w, int(w * max_ratio)))
w, h = im.size
# صغّر واحفظ JPEG — الـ PNG بدقة 2x بيطلع ملف ضخم من غير فايدة في الطباعة
target_w = 1700
if w > target_w:
im = im.resize((target_w, int(h * target_w / w)), Image.LANCZOS)
w, h = im.size
path = path.replace('.png', '_o.jpg')
im.save(path, 'JPEG', quality=72, optimize=True)
dw = W
dh = dw * h / w
if dh > maxh:
dh = maxh; dw = dh * w / h
img = RLImage(path, width=dw, height=dh)
t = Table([[img]], colWidths=[dw])
t.setStyle(TableStyle([("BOX", (0,0), (-1,-1), 0.7, LINE),
("TOPPADDING", (0,0), (-1,-1), 0), ("BOTTOMPADDING", (0,0), (-1,-1), 0),
("LEFTPADDING", (0,0), (-1,-1), 0), ("RIGHTPADDING", (0,0), (-1,-1), 0)]))
return t
def footer(canvas, doc):
canvas.saveState()
canvas.setFont("AR", 8)
canvas.setFillColor(GRAY)
canvas.drawCentredString(A4[0] / 2, 1.05 * cm, str(doc.page))
canvas.setFont("AR", 7.5)
canvas.drawRightString(A4[0] - 2.0 * cm, 1.05 * cm, ar("دليل المحاسبة والأصول والخزينة"))
canvas.restoreState()
def main():
man = json.load(open('/tmp/pw_build/fin_manifest.json', encoding='utf-8'))
man = [m for m in man if m.get('file')]
out = '/config/workspace/projects/clubphp/docs/دليل_المحاسبة_والأصول_والخزينة.pdf'
os.makedirs(os.path.dirname(out), exist_ok=True)
doc = SimpleDocTemplate(out, pagesize=A4,
rightMargin=MARGIN, leftMargin=MARGIN,
topMargin=1.8*cm, bottomMargin=1.8*cm,
title="دليل المحاسبة والأصول والخزينة")
st = []
# ── الغلاف ───────────────────────────────────────────────────────────
st += [Spacer(1, 5.5*cm), P("دليل المحاسبة والأصول والخزينة", "title"),
P("شرح عملي لكل شاشة بلقطات حقيقية من النظام", "sub"),
Spacer(1, 0.5*cm),
HRFlowable(width="45%", thickness=1, color=LINE, hAlign="CENTER"),
Spacer(1, 0.5*cm),
P("نظام إدارة النادي", "meta"),
P("المحاسبة العامة · الأصول الثابتة · الخزينة والكاشير", "meta"),
PageBreak()]
# ── الفهرس ───────────────────────────────────────────────────────────
st += [P("المحتويات", "h2"), Spacer(1, 0.25*cm)]
seen = []
for m in man:
if m['sec'] not in seen:
seen.append(m['sec'])
st.append(P(m['sec'], "toc_h"))
st.append(P("• " + m['l'], "toc"))
st.append(PageBreak())
# ── الشاشات ──────────────────────────────────────────────────────────
cur = None
for m in man:
if m['sec'] != cur:
if cur is not None:
st.append(PageBreak())
cur = m['sec']
st += [band(cur, m['color']), Spacer(1, 0.45*cm)]
blk = [P(m['l'], "h2"), Paragraph(m['p'], S['path']), P(m['c'], "cap"),
shot(f"/tmp/fin_shots/{m['file']}"), Spacer(1, 0.75*cm)]
st.append(KeepTogether(blk))
doc.build(st, onFirstPage=lambda c, d: None, onLaterPages=footer)
size = os.path.getsize(out) / 1024 / 1024
print(f"{out}\n{len(man)} screens, {size:.1f} MB")
if __name__ == '__main__':
main()
const { chromium } = require('playwright');
const fs = require('fs');
const BASE = 'https://clubmanagement.caprover.al-arcade.com';
const screens = JSON.parse(fs.readFileSync('/tmp/pw_build/fin_screens.json', 'utf8'));
const OUT = '/tmp/fin_shots';
if (!fs.existsSync(OUT)) fs.mkdirSync(OUT, { recursive: true });
async function login(page) {
await page.goto(`${BASE}/login`, { waitUntil: 'domcontentloaded', timeout: 40000 });
await page.fill('input[name=username], input[type=text]', 'admin');
await page.fill('input[name=password], input[type=password]', 'Alarcade123#');
await page.click('button[type=submit]');
await page.waitForLoadState('domcontentloaded', { timeout: 40000 });
return !page.url().includes('/login');
}
// A screen counts as captured ONLY if we are genuinely logged in on it.
async function isLoginPage(page) {
if (page.url().includes('/login')) return true;
const n = await page.locator('input[name=password], input[type=password]').count();
if (n > 0 && (await page.locator('form').count()) > 0) {
const body = await page.textContent('body').catch(() => '');
if (/تسجيل الدخول|اسم المستخدم/.test(body)) return true;
}
return false;
}
(async () => {
const browser = await chromium.launch({ args: ['--no-sandbox', '--disable-setuid-sandbox', '--disable-dev-shm-usage'] });
const ctx = await browser.newContext({ viewport: { width: 1500, height: 950 }, deviceScaleFactor: 2 });
const page = await ctx.newPage();
// شريط الـ debug مالوش لازمة في دليل تعليمي
await ctx.addInitScript(() => {
const css = `#debug_toolbar,.debug-toolbar,[id*="debug-bar"],[class*="debug-bar"],
#debugbar,.debugbar{display:none !important;}`;
document.addEventListener('DOMContentLoaded', () => {
const st = document.createElement('style'); st.textContent = css;
document.head.appendChild(st);
});
});
if (!(await login(page))) { console.log('LOGIN FAILED'); process.exit(1); }
console.log('login ok\n');
const manifest = [];
for (let i = 0; i < screens.length; i++) {
const s = screens[i];
const file = `${String(i + 1).padStart(3, '0')}.png`;
let ok = false, note = '';
for (let attempt = 1; attempt <= 3 && !ok; attempt++) {
try {
const resp = await page.goto(BASE + s.p, { waitUntil: 'networkidle', timeout: 40000 });
await page.waitForTimeout(600);
if (await isLoginPage(page)) {
note = `logged out (attempt ${attempt}) — re-login`;
await page.waitForTimeout(2500);
if (!(await login(page))) { await page.waitForTimeout(8000); await login(page); }
continue; // retry this screen
}
const body = await page.textContent('body').catch(() => '');
if (/Exception:|View not found|Fatal error/i.test(body)) {
manifest.push({ ...s, file: null, broken: true, note: 'PHP error' });
console.log(`BROKEN ${s.p}`);
ok = true; break;
}
await page.evaluate(() => {
document.querySelectorAll('*').forEach(el => {
const t = (el.textContent || '').trim();
if (/^DEBUG\b/.test(t) && t.length < 200 && el.children.length < 12) el.style.display = 'none';
});
}).catch(() => {});
await page.screenshot({ path: `${OUT}/${file}`, fullPage: true });
manifest.push({ ...s, file, broken: false, status: resp ? resp.status() : 0 });
console.log(` ok ${s.p}${note ? ' (' + note + ')' : ''}`);
ok = true;
} catch (e) {
note = String(e).slice(0, 70);
await page.waitForTimeout(3000);
}
}
if (!ok) {
manifest.push({ ...s, file: null, broken: true, note });
console.log(` FAIL ${s.p}${note}`);
}
}
fs.writeFileSync('/tmp/pw_build/fin_manifest.json', JSON.stringify(manifest, null, 1));
const good = manifest.filter(m => m.file);
console.log(`\ncaptured ${good.length}/${screens.length}`);
manifest.filter(m => !m.file).forEach(b => console.log(' MISSING: ' + b.p + ' — ' + (b.note || '')));
await browser.close();
process.exit(good.length === screens.length ? 0 : 1);
})();
# -*- coding: utf-8 -*-
"""شاشات دليل المحاسبة والأصول والخزينة — مع شرح استخدام كل شاشة."""
# (path, label, caption) — القسم بيتحدد من SECTIONS تحت
SECTIONS = [
("البداية: إعداد النظام المحاسبي", "#0D7377", [
("/accounting", "لوحة تحكم الحسابات",
"أول شاشة تفتحها كل يوم. بتلخّص حركة الشهر: الإيرادات، المصروفات، عدد القيود المسودة اللي لسه محتاجة اعتماد، "
"وتنبيه لو فيه عمليات مالية اتسجلت في النظام ولسه ما اترحّلتش للدفتر العام."),
("/accounting/fiscal-years", "السنوات المالية",
"السنة المالية هي الإطار الزمني لكل القيود. لازم تكون فيه سنة مفتوحة قبل ما تقدر تسجّل أي قيد. "
"من هنا بتشوف حالة كل سنة (مفتوحة / مقفلة) وتواريخ بدايتها ونهايتها."),
("/accounting/fiscal-years/create", "إضافة سنة مالية",
"بتتعمل مرة واحدة في بداية كل سنة: اسم السنة وتاريخ البداية والنهاية. "
"ما تقفلش السنة القديمة غير بعد ما تتأكد إن كل قيودها مرحّلة."),
("/accounting/chart-of-accounts", "دليل الحسابات",
"شجرة حسابات النادي كلها: الأصول، الخصوم، حقوق الملكية، الإيرادات، المصروفات — بترقيم هرمي. "
"أي حساب رئيسي (Header) ما ينفعش تترحّل عليه قيود مباشرة، الترحيل بيكون على الحسابات الفرعية بس."),
("/accounting/chart-of-accounts/create", "إضافة حساب جديد",
"اختار الحساب الأب الأول، والنظام بيقترح عليك الكود التالي تلقائيًا. "
"حدّد نوع الحساب (أصل/خصم/إيراد/مصروف) وهل هو حساب رئيسي ولا فرعي يترحّل عليه."),
("/accounting/journal-types", "أنواع القيود",
"تصنيفات القيود (عادي، افتتاحي، إقفال، تسوية) — بتستخدم لتنظيم دفتر اليومية وفلترة التقارير."),
("/accounting/cost-centers", "مراكز التكلفة",
"مراكز التكلفة بتقسّم الإيرادات والمصروفات على الفروع أو الأنشطة، عشان تعرف ربحية كل واحد لوحده. "
"بتتحدد على مستوى سطر القيد."),
("/accounting/budgets", "الموازنات التقديرية",
"الموازنة السنوية المتوقعة لكل حساب أو مركز تكلفة. بتتدخل مرة في بداية السنة وبتتقارن بالفعلي طول السنة."),
("/accounting/budgets/variance", "انحرافات الموازنة",
"المقارنة بين المخطط والفعلي لكل بند: تشوف فين الصرف عدّى الموازنة وبكام، وفين فيه وفر."),
("/accounting/dimensions", "الأبعاد التحليلية",
"تصنيفات إضافية للتحليل أعمق من مركز التكلفة. ملاحظة مهمة: الأبعاد دي معرّفة في النظام "
"لكن لسه مش مربوطة بترحيل القيود، فاعتبرها إعداد مستقبلي."),
]),
("القيود اليومية ودفتر الأستاذ", "#2563EB", [
("/accounting/journal-entries", "دفتر اليومية",
"كل القيود: اللي بتتكتب بإيدك واللي النظام بيولّدها لوحده من المدفوعات والمبيعات والرواتب. "
"القيد المسودة (Draft) ما بيأثرش على الأرصدة غير بعد الترحيل."),
("/accounting/journal-entries/create", "إنشاء قيد يومية",
"اكتب التاريخ والبيان، وبعدين ضيف السطور: كل سطر له حساب ومدين أو دائن. "
"النظام مش هيسمح بحفظ القيد غير لما إجمالي المدين يساوي إجمالي الدائن بالظبط."),
("/accounting/opening-entries", "القيود الافتتاحية",
"أرصدة أول المدة لما تبدأ تشغّل النظام لأول مرة، أو في بداية سنة مالية جديدة بعد الترحيل من السنة اللي فاتت."),
("/accounting/daily-transactions", "الحركة اليومية",
"كل الحركات المالية اللي حصلت في يوم معين من كل أنحاء النظام — مفيدة للمراجعة اليومية قبل الإقفال."),
("/accounting/sync-gl/preview", "معاينة الترحيل للدفتر العام",
"بتعرض كل العمليات (مدفوعات، غرامات، أقساط، مبيعات، رواتب) اللي لسه ما اتحوّلتش لقيود. "
"راجعها هنا الأول، وبعدين رحّل."),
("/accounting/period-closing", "إقفال الفترة",
"بعد ما تتأكد إن كل قيود الشهر مرحّلة ومتوازنة، بتقفل الفترة عشان محدش يعدّل فيها بأثر رجعي."),
("/accounting/posting-chains", "سلاسل الترحيل",
"بتوضّح إزاي كل نوع عملية بيتحوّل لقيد، وعلى أنهي حسابات. لو عملية مش بتترحّل صح، ابدأ التشخيص من هنا."),
("/accounting/posting-chains/parked", "العمليات المعلّقة",
"عمليات وقفت في نص سلسلة الترحيل ومحتاجة تدخّل — زي عملية حسابها الافتراضي مش متظبط."),
("/accounting/accruals", "الاستحقاقات",
"المصروفات أو الإيرادات اللي تخص الفترة الحالية بس لسه ما اتدفعتش أو ما اتحصّلتش — عشان القوائم تعبّر عن الفترة صح."),
("/accounting/gaps", "أداة الفجوات",
"بتدوّر على العمليات اللي المفروض يكون ليها أثر محاسبي وما حصلش. أهم جزء فيها الربط المكسور: "
"لاعب متسجّل برقم مش رقم عضو حقيقي، فالدين مش بيظهر على حساب حد. "
"زرار «صلّح الربط» بيفتح ملف اللاعب نفسه وتدوّر على العضو بالاسم وتختاره."),
]),
("البنوك والشيكات والأوراق التجارية", "#7C3AED", [
("/accounting/bank-accounts", "الحسابات البنكية",
"حسابات النادي في البنوك بأرصدتها. كل حساب مربوط بحساب في دليل الحسابات، "
"وده اللي بيخلي الإيداعات تترحّل صح."),
("/accounting/instruments", "الشيكات والأوراق التجارية",
"كل الشيكات: المستلمة من الأعضاء والموردين، والمُصدرة من النادي، بحالتها الحالية."),
("/accounting/instruments/due-soon", "شيكات قرب استحقاقها",
"الشيكات اللي تاريخ استحقاقها قرب — تابعها عشان تحصّلها أو توفّر رصيدها في وقتها."),
("/accounting/instruments/bounced", "الشيكات المرتدة",
"الشيكات اللي ارتدّت من البنك، بسبب الارتداد وعدد مرات ارتداد كل شيك، وإجراء التسوية."),
("/accounting/portfolios", "محافظ التحصيل",
"تجميع الشيكات في محافظ بتتسلّم للبنك مرة واحدة للتحصيل، بدل ما تتسلّم واحد واحد."),
("/accounting/bank-reconciliation", "التسوية البنكية",
"مطابقة كشف حساب البنك بدفاترك: تحدّد الفروقات (شيكات لسه ما اتصرفتش، إيداعات في الطريق، مصاريف بنكية) "
"لحد ما الرصيدين يتطابقوا."),
("/accounting/notes-receivable", "أوراق القبض",
"الأوراق التجارية المستحقة للنادي على الغير."),
("/accounting/notes-payable", "أوراق الدفع",
"الأوراق التجارية المستحقة على النادي للغير."),
]),
("الاعتمادات والضمانات والقروض", "#D97706", [
("/accounting/loans", "القروض البنكية",
"قروض النادي من البنوك: المبلغ الأصلي، سعر الفائدة، المدة، والرصيد القائم."),
("/accounting/loans/create", "إضافة قرض",
"سجّل القرض بمبلغه وسعر فائدته ومدته، والنظام بيولّد جدول السداد بالأقساط والفوائد."),
("/accounting/loans/{loans}", "تفاصيل القرض وجدول السداد",
"كل قسط بتاريخ استحقاقه وأصله وفائدته وحالته (مسدد / مستحق / متأخر)."),
("/accounting/guarantees", "خطابات الضمان",
"خطابات الضمان الصادرة من البنك لصالح جهات تانية — ابتدائي، نهائي، دفعة مقدمة، صيانة، جمركي."),
("/accounting/guarantees/create", "إصدار خطاب ضمان",
"حدّد البنك والمستفيد ونوع الخطاب وقيمته ونسبة الغطاء والعمولة وتاريخ الانتهاء. "
"رقم الخطاب بيتولّد تلقائيًا."),
("/accounting/guarantees/{guarantees}", "تفاصيل خطاب الضمان",
"بيانات الخطاب كاملة مع الغطاء والعمولة. ومن نفس الشاشة بتحدّث حالته "
"(صادر / ساري / مُفرج عنه / تم تسييله / منتهي / مُجدد). "
"لو اخترت «مُجدد» النظام هيطلب منك تاريخ انتهاء جديد."),
("/accounting/documentary-credits", "الاعتمادات المستندية",
"الاعتمادات المفتوحة لاستيراد بضاعة أو معدات من مورد خارجي."),
("/accounting/documentary-credits/create", "فتح اعتماد مستندي",
"حدّد البنك المُصدر والمستفيد وقيمة الاعتماد والعملة ونسبة الغطاء وتاريخ الانتهاء."),
("/accounting/documentary-credits/{documentary-credits}", "تفاصيل الاعتماد والمستندات",
"قيمة الاعتماد والغطاء والمصروفات، وجدول المستندات المطلوبة (فاتورة، بوليصة شحن، شهادة منشأ...) "
"وحالة كل مستند. ومن هنا كمان بتحدّث حالة الاعتماد."),
("/accounting/settlements", "التسويات",
"تسوية الحسابات المتبادلة بين النادي والجهات الخارجية."),
]),
("التقارير المالية والقوائم", "#059669", [
("/accounting/reports/trial-balance", "ميزان المراجعة",
"كل الحسابات بأرصدتها المدينة والدائنة. لازم إجمالي المدين = إجمالي الدائن. "
"لو مش متساويين يبقى فيه قيد غير متوازن محتاج مراجعة."),
("/accounting/reports/general-ledger", "دفتر الأستاذ العام",
"كل حركات حساب معيّن بالتفصيل خلال فترة، برصيد متحرك — عشان تعرف رصيد الحساب جه منين بالظبط."),
("/accounting/reports/income-statement", "قائمة الدخل",
"الإيرادات ناقص المصروفات = صافي الربح أو الخسارة عن الفترة."),
("/accounting/reports/balance-sheet", "الميزانية العمومية",
"المركز المالي في تاريخ معيّن: الأصول = الخصوم + حقوق الملكية."),
("/accounting/reports/consolidated-balance-sheet", "الميزانية المجمّعة",
"نفس الميزانية بس مجمّعة على مستوى كل الفروع مع بعض."),
("/accounting/reports/cash-flow", "قائمة التدفقات النقدية",
"حركة الكاش الفعلية داخل وخارج النادي: تشغيلية، استثمارية، وتمويلية."),
("/accounting/reports/equity-statement", "قائمة التغير في حقوق الملكية",
"التغيرات على حقوق الملكية خلال الفترة."),
("/accounting/reports/accounts-receivable", "تقرير المدينين",
"سطر واحد لكل مدين بإجمالي مديونيته وتوزيعها على فترات التقادم (جاري، 30، 60، 90، أكتر من 90). "
"اضغط على أي مدين تتفتح لك تفاصيل كل مطالباته."),
("/accounting/reports/accounts-payable", "تقرير الدائنين",
"نفس الفكرة بس للموردين: المستحق لكل مورد وتقادمه."),
("/accounting/reports/member-statement", "كشف حساب عضو",
"كل الحركات المالية لعضو واحد: المطالبات والمدفوعات برصيد متحرك. بتدوّر على العضو بالاسم أو رقم العضوية."),
("/accounting/reports/treasury", "تقرير الخزينة",
"حركة النقدية في الخزائن وأرصدتها."),
("/accounting/reports/revenue-analysis", "تحليل الإيرادات",
"الإيرادات مقسّمة حسب المصدر — عشان تعرف النشاط اللي بيجيب أكتر."),
("/accounting/statements/daily-cash", "حركة النقدية اليومية",
"الوارد والمنصرف نقدًا في اليوم — بتستخدم في المطابقة اليومية للخزينة."),
]),
("الأصول الثابتة", "#BE185D", [
("/inventory/assets", "سجل الأصول الثابتة",
"كل أصول النادي: المباني، المعدات، الأجهزة، السيارات — بقيمتها الدفترية الحالية وحالتها."),
("/inventory/assets/create", "إضافة أصل",
"سجّل الأصل بتاريخ شرائه وقيمته والعمر الإنتاجي وطريقة الإهلاك. "
"من التاريخ ده النظام بيحسب الإهلاك ويرحّله."),
("/inventory/assets/{assets}", "تفاصيل الأصل",
"بيانات الأصل كاملة: قيمة الشراء، مجمّع الإهلاك، القيمة الدفترية، وتاريخه من صيانة وعهدة."),
("/inventory/assets/maintenance", "صيانة الأصول",
"سجل الصيانات: الدورية والطارئة، بتكلفتها وتاريخها — والتكلفة دي بتترحّل مصروف."),
("/inventory/assets/custody", "عهدة الأصول",
"مين مستلم أنهي أصل. أي أصل لازم يكون في عهدة موظف محدد يبقى مسؤول عنه."),
("/inventory/suppliers", "الموردون",
"بيانات الموردين اللي النادي بيشتري منهم، وأرصدتهم المستحقة."),
]),
("الخزينة والكاشير", "#0891B2", [
("/treasury", "لوحة الخزينة الفرعية",
"رصيد العهدة الحالي، تحصيلات اليوم، وعدد الطلبات المعلّقة في الطابور. "
"لازم تفتح وردية الأول قبل ما تقدر تحصّل أي حاجة."),
("/treasury/queue", "طابور التحصيل",
"كل طلبات الدفع اللي الموديولات التانية بعتتها للخزينة ولسه ما اتحصّلتش."),
("/treasury/sessions", "الورديات",
"سجل كل الورديات: مين فتحها وإمتى، وكام حصّل فيها، وحالتها (مفتوحة / مقفلة / متسوّاة)."),
("/treasury/sessions/current", "الوردية الحالية",
"تفاصيل ورديتك المفتوحة دلوقتي. لما تخلص شغل بتقفلها، والنظام بيحسب إجمالي النقدي اللي المفروض معاك."),
("/treasury/settlements", "التسويات",
"تسليم نقدية الوردية المقفلة من الخزينة الفرعية للخزينة الرئيسية."),
("/treasury/settlements/create", "إجراء تسوية",
"بتختار وردية مقفلة وتعمل تسوية بقيمتها النقدية. التسوية بتفضل «معلّقة» لحد ما "
"الخزينة الرئيسية تستلمها وتأكّدها."),
("/treasury/custody", "سجل العهدة",
"كشف حركة العهدة النقدية: كل تحصيل بيزوّدها وكل تسوية أو إيداع بيقلّلها، برصيد متحرك."),
("/cashier", "خزنة العضويات — طابور الدفع",
"نفس فكرة الخزينة بس لرسوم العضوية والأنشطة: رسوم استمارة، قيمة عضوية، مقدم تقسيط، "
"رسوم إضافة تابعين، غرامات، اشتراكات سنوية."),
("/cashier/settlements", "استلام التسويات",
"شاشة الخزينة الرئيسية لاستلام النقدية الجاية من الخزائن الفرعية — بتستلم أو ترفض مع ذكر السبب."),
("/cashier/deposits", "الإيداعات البنكية",
"تسجيل إيداع نقدية الخزينة الرئيسية في البنك."),
("/cashier/deposits/create", "إيداع جديد",
"اختار الحساب البنكي والمبلغ، وارفع صورة إيصال البنك. "
"الإيداع بيفضل محتاج تأكيد لحد ما يتراجع ويتأكّد."),
]),
]
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment