Skip to Content
Go Realm v1 is released 🎉
Accountingবই (Print Edition)অধ্যায় ২৬: Opening, Closing ও Period Closing

অধ্যায় ২৬ — Opening, Closing ও Period Closing

Volume 1 · Part 3 — Financial Statements · Chapter 26

পূর্বশর্ত: অধ্যায় ১ (accounting_periods, fiscal_year_start), অধ্যায় ৩ (temporary account শূন্য হয়), অধ্যায় ৯ (OB voucher, snapshot), অধ্যায় ২৩ (3200/3300-এর দরজা), অধ্যায় ২৪–২৫ (pack, checklist)


১. Learning Objective

এই অধ্যায় শেষে আপনি পারবেন:

Period-এর জীবনচক্র — open → soft-closed → closed → locked — প্রতিটি অবস্থায় কী পারা যায়, কে বদলায়, কী log হয় ক্রমের নিয়ম: আগেরটা বন্ধ না হলে পরেরটা নয়; পরেরটা বন্ধ থাকলে আগেরটা খোলা যায় না — কেন Reopen — কখন বৈধ, কী হারায় (pack superseded, snapshot stale), কী থাকে (log) বছরের শেষে closing entry — temporary → 3300 → 3200 — দুই ধাপ, একবার, idempotent, উল্টানো যায় Pre-closing আর post-closing Trial Balance — দুটোই দরকার, কেন Closing entry কোথায় বসে যাতে জুনের Income Statement শূন্য না দেখায় — closing period / period 13 Opening — continuous ledger বনাম OB carry-forward; প্রথম দিনের OB entry (migration-এর আগাম) Audit adjustment বছর শেষের মাস পরে — ১৩তম period, মাসের pack অক্ষত, বছরের pack version 2 Comparative column বছর পেরিয়ে — closing entry-র পরেও গত বছরের Income Statement ঠিক Proprietorship-এ drawings → capital; company-তে dividend closing-এর আগে

সময়: পড়া ৪৫ মিনিট + অনুশীলন ৫০ মিনিট।


২. Concept Explanation

Period — একটা state machine

অধ্যায় ১-এর accounting_periods.status তিনটি ছিল: open, closed, locked। অধ্যায় ২৪-এর pack একটা মাঝের ধাপ চাইল। পুরো জীবনচক্র:

future ──▶ open ──▶ soft_closed ──▶ closed ──▶ locked ▲ │ │ └───────────┴─────────────┘ reopen (approved, logged) locked থেকে কখনো নয় অবস্থা কে post করতে পারে কী ঘটায় ─────────── ───────────────────────────────────── ───────────────────────────────── future কেউ নয় (তারিখ এখনো আসেনি; PDC-র due date ব্যতিক্রম নয় — post clearing-এ) open সবাই (permission-মতো) স্বাভাবিক soft_closed শুধু adjusting (অধ্যায় ২৫) ও approved checklist চলছে / pack review-এ; document আর নয় closed কেউ নয় pack final; Balance Sheet "ছাপা" locked কেউ নয়, reopen-ও নয় audit শেষ; চিরস্থায়ী

soft_closed-এর দরকার কেন: checklist (অধ্যায় ২৫) চলার সময় কেউ bill post করলে ধাপ ৩-এর যাচাই বাসি — অথচ হিসাবরক্ষকের adjusting entry দরকার। তাই দুই স্তর: document বন্ধ, হিসাব খোলা।

ক্রম — period-রা একা নয়

নিয়ম ১: period N close করা যাবে শুধু যদি N−1 closed (বা locked) নিয়ম ২: period N reopen করা যাবে শুধু যদি N+1 … শেষ পর্যন্ত সব open (বা future)

দ্বিতীয়টা কম স্পষ্ট, বেশি জরুরি। নভেম্বর reopen করে একটা bill post করলে নভেম্বরের closing বদলায় → ডিসেম্বরের opening বদলায় → ডিসেম্বরের Balance Sheet বদলায় — অথচ ডিসেম্বর closed, pack final, ব্যাংকে গেছে। Balance Sheet-এর “closing = opening + movement” (অধ্যায় ১০) মানে একটা period-এর সংখ্যা তার আগের সব period-এর উপর দাঁড়িয়ে। তাই পিছনে যেতে হলে সামনের সব খুলতে হয় — অথবা পিছনে না গিয়ে সামনে post (অধ্যায় ১৮-এর DESCO-র bill নভেম্বরে, অক্টোবরে নয়)।

বাস্তবে reopen বিরল: material error, period-এর pack এখনো বাইরে যায়নি, পরের period খোলা। বাকি সব — চলতি period-এ, posting_date আজ, narration-এ “relates to Nov” (অধ্যায় ৪৩)।

Closing entry — বছর শেষে একবার

অধ্যায় ৩-এর প্রতিশ্রুতি: temporary account (4xxx, 5xxx) বছর শেষে শূন্য, নিট 3200-এ। এই বইয়ে দুই ধাপে (অধ্যায় ২১, ২৩-এ 3300 আলাদা রাখার কারণ):

ধাপ ১ — temporary → 3300: প্রতিটি REVENUE account: Dr (তার credit balance) } প্রতিটি EXPENSE account: Cr (তার debit balance) } একটা entry, বহু line Cr 3300 Current Year Profit (পার্থক্য = বছরের লাভ) } ক্ষতি হলে Dr 3300 ধাপ ২ — 3300 → 3200: Dr 3300 / Cr 3200 Retained Earnings (লাভ) proprietorship: + Dr 3100 Capital / Cr 3400 Drawings (drawings capital-এ)

এক entry-তেও করা যায় (সরাসরি 3200-এ); দুই ধাপে রাখার লাভ — ledger-এ “বছরের লাভ” একটা সংখ্যা হিসেবে দেখা যায়, আর SOCE-র transfer row (অধ্যায় ২৩) স্পষ্ট। ফল:

closing-এর পরে: 4xxx, 5xxx সব শূন্য; 3300 শূন্য; 3200 += বছরের লাভ; Balance Sheet অপরিবর্তিত (equity-র ভিতরে সরল) পরের বছর: Income Statement শূন্য থেকে; movement() এমনিতেই (নিচের সমস্যা ছাড়া)

কখন? বছর শেষের দিন নয় — audit-এর পরে, মাস দুই-তিন পরে। মাঝে “বছর শেষ কিন্তু close হয়নি” অবস্থা — 3300 computed (অধ্যায় ২১) সেই সময়েও ঠিক Balance Sheet দেয়। Closing entry report-এর জন্য দরকার নয়; দরকার ledger পরিষ্কার রাখতে আর “এ বছর” শব্দটার সীমানা টানতে।

Closing entry-র জায়গা — জুনের Income Statement যেন শূন্য না হয়

সমস্যা: closing entry-র posting_date ৩০ জুন। অধ্যায় ২০-এর movement(01-06, 30-06) জুনের Income Statement বানাতে গিয়ে closing entry-ও গুনবে — 4110-এ বছরের পুরো credit-এর সমান debit — জুনের revenue ঋণাত্মক। তিনটি সমাধান:

ক. movement()-এ source_type = 'closing_entry' বাদ (IS-এর জন্য সবসময়) খ. closing entry একটা আলাদা period-এ — period_no 13 "Closing", তারিখ ৩০ জুন, is_closing = true; IS-এর query period-ধরে হলে 13 বাদ; তারিখ-ধরে হলে ক-ও লাগে গ. closing entry-র তারিখ ০১ জুলাই (পরের বছরের প্রথম দিন) — জুলাইয়ের IS ঋণাত্মক — না

এই বইয়ে ক + খ দুটোই: closing entry period 13-এ, source_type = 'closing_entry', আর movement() default-এ closing_entry বাদ (Balance Sheet-এর closing()-এ বাদ নয় — 3200 ঠিক থাকতে হবে)। Period 13 আরেকটা কাজেও লাগে — নিচে।

Pre-closing আর post-closing Trial Balance

pre-closing TB (৩০ জুন, closing entry ছাড়া) সব account; 4xxx/5xxx বছরের মোট; অধ্যায় ২৪-এর worksheet এটা থেকেই post-closing TB (৩০ জুন, closing entry সহ) শুধু permanent; 4xxx/5xxx শূন্য; 3200 নতুন যাচাই: post-closing TB == পরের বছরের ১ জুলাইয়ের opening (অধ্যায় ১০-এর "জুন-জুলাই" যাচাই)

দুটোই ছাপা হয় — pre-closing auditor-এর জন্য (বছরের কাজ), post-closing পরের বছরের শুরুর জন্য।

১৩তম period — বছরের পরে বছরের entry

Audit সেপ্টেম্বরে; auditor বলেন “৩০ জুনের provision 50,000 কম”। জুন closed, pack final, বারো মাসের pack-ও। কোথায় post?

জুনে (reopen) বারো মাসের pack, জুনের pack — সব superseded; জুলাই-আগস্টের opening বদলায়; না সেপ্টেম্বরে বছরের Balance Sheet ভুলই থাকে (৩০ জুনে provision নেই); আইনত সেটাই ছাপা হবে; না period 13, ৩০ জুন বছরের Balance Sheet ঠিক (৩০ জুন closing-এ আছে); জুনের মাসিক pack অক্ষত (মাসের IS period-ধরে, 13 বাদ); বছরের pack version 2; জুলাইয়ের opening বদলায় — কিন্তু জুলাই তখনো open (audit চলছে) — ঠিক আছে

Period 13: is_adjustment = true, start = end = ৩০ জুন, status নিজের। শুধু adjusting/audit entry, approved। বছরের IS (period 1–13) আর মাসের IS (period 1–12 আলাদা) — দুটোর পার্থক্যই “audit adjustments” — note-এ। Closing entry-ও এই period-এ, সবার শেষে।

Opening — ledger চলমান, না বহন

দুটো ধারা:

continuous ledger closing(as_of) = Σ posted_lines WHERE date <= as_of — প্রথম দিন থেকে; OB entry শুধু প্রথম দিনে carry-forward প্রতি বছরের শুরুতে OB entry (গত বছরের post-closing TB); বছরের query বছরের ভিতরে

এই বইয়ে continuous — সরল, একটা সত্য, অধ্যায় ২১-এর closing() এটাই ধরে। দাম: দশ বছর পরে closing() দশ বছরের line যোগ করে — অধ্যায় ৯-এর snapshot (account_period_balances) সেটা মেটায়: period 1-এর opening_signed = গত বছরের post-closing; query snapshot থেকে, raw শুধু চলতি period-এ। ফল carry-forward-এর মতো, সত্য continuous-এর। Carry-forward-এর আসল ব্যবহার: প্রথম দিন — system-এ আসার দিন আগের সব হিসাবের একটা OB entry (অধ্যায় ৯-এর OB voucher); বিস্তারিত অধ্যায় ৬২ (migration)।

OB entry-র জায়গা: period 0 (is_opening = true), তারিখ অর্থবছর শুরুর আগের দিন — যাতে বছরের movement()-এ না ঢোকে, closing()-এ ঢোকে। Period 13-এর আয়না।

Lock — audit-এর পরে চিরতরে

closed থেকে locked: audit report সই, statement জমা। Locked period reopen হয় না — কোনো permission-এই না। ভুল পাওয়া গেলে prior-period adjustment (অধ্যায় ২৩) চলতি বছরে, restated comparative। এটা technical নয়, আইনি — ছাপা statement বদলায় না; software শুধু সেটা মানে। Lock-এর পরে snapshot-ও immutable (is_stale কখনো true হবে না — হলে bug)।

যা এই অধ্যায়ে নেই

Period lock-এর architecture (permission, API, concurrency, bypass-এর audit trail) অধ্যায় ৪২ Migration-এর OB (subledger-সহ, open item-সহ, mid-year) অধ্যায় ৬২ Multi-company year-end (একই দিনে ৫টা) অধ্যায় ৬০ Tax provision-এর হিসাব (closing-এর আগে শেষ adjusting) অধ্যায় ৫৫

৩. Accounting Rule

Period status

future → open → soft_closed → closed → locked; reopen: soft_closed/closed → open (approved, logged); locked: কখনো নয় open: সব; soft_closed: adjusting + approved; closed / locked: কিছু নয় close N ⇐ N−1 closed; reopen N ⇐ N+1 … সব open/future

Closing entry

কখন: audit-এর পরে, period 13-এ, তারিখ = FY শেষ দিন, সবার শেষে ধাপ ১: Dr প্রতিটি REVENUE (balance), Cr প্রতিটি EXPENSE (balance), Cr/Dr 3300 (নিট) ধাপ ২: Dr 3300 / Cr 3200 (লাভ); ক্ষতি উল্টো; proprietorship: Dr 3100 / Cr 3400 source_type = 'closing_entry'; idempotency_key = ('closing_entry', fy); উল্টানো = reopenYear() ফল: 4xxx, 5xxx, 3300 = 0; 3200 += লাভ; Balance Sheet মোট অপরিবর্তিত

Report-এ closing entry

movement() (Income Statement, SOCE-র profit) → closing_entry বাদ; period 13 বাদ (মাসের report-এ) closing() (Balance Sheet) → সব সহ বছরের IS = period 1–13; মাসের IS = নিজের period; পার্থক্য = audit adjustments (note)

Trial Balance

pre-closing: closing entry ছাড়া; post-closing: সহ; post-closing == পরের বছরের opening

Opening

continuous ledger; snapshot-এ period 1 opening = গত post-closing প্রথম দিন: OB entry, period 0, তারিখ FY শুরুর আগের দিন, balanced (অধ্যায় ৬২)

Period 13 / 0

13: is_adjustment, start = end = FY শেষ; শুধু adjusting/audit + closing entry; approved 0: is_opening, start = end = FY শুরু − 1; শুধু OB

৪. Real Business Example

FY2026 (জুলাই ২০২৫ – জুন ২০২৬) — বছরের শেষ

ধরা যাক জানুয়ারি–জুন ২০২৬-এ লাভ 2,40,000 (অধ্যায় ২৩-এর exercise ১); বছরের মোট revenue (সব 4xxx, নিট) 68,40,000, expense (সব 5xxx) 63,21,635 → লাভ 5,18,365 (= ডিসেম্বর পর্যন্ত 2,78,365 + 2,40,000)। মালিক এ বছর পুঁজি দেননি, dividend নেই।

তারিখের ক্রম:

৩০-০৬-২০২৬ বছরের শেষ দিন; জুন open ০৩-০৭ জুনের checklist (অধ্যায় ২৫) — reversal ০১-০৭ হয়ে গেছে; জুন soft_closed ০৮-০৭ জুনের pack v1 reviewed → final; জুন closed; জুলাই open (০১-০৭ থেকেই) ১০-০৭ বছরের pack v1 (period 1–12) — draft; auditor-কে; 3300 computed 5,18,365, 3200 1,20,000 ২০-০৯ auditor: ৩০ জুনে warranty provision 50,000 নেই (অধ্যায় ৫৯-এর প্রকার); audit fee accrual 75,000 কম ২২-০৯ period 13 (FY2026, তারিখ ৩০-০৬) খোলা; দুটো audit adjustment post, approved CFO + auditor ref ২৫-০৯ বছরের pack v2 (period 1–13): লাভ 5,18,365 − 1,25,000 = 3,93,365; reviewed → final ২৫-০৯ closeYear(FY2026): closing entry period 13-এ; period 13 closed ৩০-০৯ audit report সই; FY2026-এর সব period locked

Pre-closing Trial Balance — ৩০ জুন ২০২৬, period 1–13 (অংশ)

Code Account Dr Cr ──── ───────────────────────── ────────── ────────── … (permanent accounts) … … 2135 Accrued Expenses 1,35,500 ← জুনের 60,500 + audit fee 75,000 2190 Warranty Provision 50,000 ← period 13 3100 Share Capital 10,00,000 3200 Retained Earnings 1,20,000 ← এখনো গত বছরের 3300 Current Year Profit 0 ← TB-তে নেই; report-এ computed 3,93,365 4110 Software Development Income 64,20,000 4120 Maintenance & Support Income 1,20,000 4130 Consulting Income 2,46,000 4180 Sales Returns & Allowances 54,000 … (বাকি 4xxx, 5xxx) … … 5270 Professional Fee 4,05,000 ← + 75,000 audit fee (period 13) 5540 Warranty Expense 50,000 ← period 13 ────────── ────────── Σ REVENUE (নিট) 68,40,000 Σ EXPENSE 64,46,635 লাভ (period 1–13) 3,93,365

মাসিক pack-গুলোর যোগ (period 1–12) 5,18,365; বছরের (1–13) 3,93,365; পার্থক্য 1,25,000 = period 13 — বছরের pack-এর note: “audit adjustments: provision 50,000, audit fee 75,000”।

Closing entry — period 13, ২৫-০৯, তারিখ ৩০-০৬

JV-13-0003 source = closing_entry, idempotency_key = ('closing_entry', 2026), period 13 ধাপ ১ Dr 4110 Software Development Income 64,20,000 Dr 4120 Maintenance & Support Income 1,20,000 Dr 4130 Consulting Income 2,46,000 Dr 4510, 4540 … … Cr 4180 Sales Returns & Allowances 54,000 ← contra: debit balance, তাই Cr Cr 4190 Discount Allowed … Cr 5110 Developer Salary — Billable 26,40,000 Cr 5120 … 5540 (প্রতিটি expense, তার balance) … Cr 3300 Current Year Profit 3,93,365 ← Σ Dr − Σ Cr ধাপ ২ Dr 3300 Current Year Profit 3,93,365 Cr 3200 Retained Earnings 3,93,365

একটা entry, ৩০-এর বেশি line, balanced (Σ revenue Dr == Σ expense Cr + লাভ)। Drawings নেই (company)। Dividend থাকলে closing-এর আগে ঘোষণা — distributable-এর হিসাব (অধ্যায় ২৩) closing-এর আগের 3300 + 3200 থেকে।

Post-closing — একই দিন, ৩০ জুন, closing entry সহ

4xxx, 5xxx সব 0 3300 0 3200 1,20,000 + 3,93,365 = 5,13,365 Balance Sheet (৩০-০৬-২০২৬) equity: 10,00,000 + 5,13,365 + 0 = 15,13,365 — closing-এর আগেও এটাই ছিল (10,00,000 + 1,20,000 + 3,93,365) post-closing TB == জুলাই ২০২৬-এর opening ✓ (account_period_balances FY2027 period 1 rebuild)

Balance Sheet-এর মোট বদলায়নি — equity-র ভিতরে 3,93,365 এক ঘর থেকে অন্য ঘরে। Closing entry কোনো report-এর সংখ্যা বদলায় না (SOCE-র transfer row ছাড়া) — শুধু ledger গোছায়।

জুলাই ২০২৬ — নতুন বছর

Income Statement (জুলাই ২০২৬): 4110 জুলাইয়ের invoice/recognition — শূন্য থেকে; closing entry period 13-এ, বাদ Income Statement (জুন ২০২৬): আগের মতোই — movement() closing_entry বাদ দেয়; period 12 শুধু Balance Sheet (৩১-০৭-২০২৬): 3200 5,13,365; 3300 জুলাইয়ের লাভ (computed, FY2027 শুরু থেকে) গত বছরের column (IS): movement(01-07-2025, 30-06-2026) — closing_entry বাদ — 5,18,365 (1–12) বা 3,93,365 (1–13, config)

শেষ লাইনটা অধ্যায় ২০-এর test — “গত বছরের column closing entry-র পরেও ঠিক” — এখানে প্রমাণ।

একটা reopen — কেন হলো না

০৫-০৮: জুলাইয়ের বই দেখতে গিয়ে হিসাবরক্ষক পেলেন — মে মাসের একটা travel bill 4,200 post হয়নি। মে closed, জুন closed, জুলাই open। নিয়ম ২: মে reopen করতে জুন-ও খুলতে হবে — জুনের pack final, বছরের pack auditor-এর হাতে। না। পথ: জুলাইয়ে post, narration “May 2026 bill, received late”, 5260 জুলাইয়ে 4,200 বেশি — অধ্যায় ১৮-এর DESCO-র নিয়ম। 4,200 material নয়; হলে period 13-এ (audit adjustment হিসেবে, auditor-এর সাথে)।


৫. Implementation — Software ও Database

accounting_periods — অধ্যায় ১-এর table, বাড়ানো

accounting_periods (+) period_no SMALLINT -- 0 (opening) | 1..12 | 13 (adjustment/closing) status VARCHAR(20) -- future | open | soft_closed | closed | locked is_opening BOOLEAN -- period 0 is_adjustment BOOLEAN -- period 13 soft_closed_at/by, closed_at/by, locked_at/by CHECK (period_no BETWEEN 0 AND 13) UNIQUE (company_id, fiscal_year, period_no)
period_status_log id, period_id, from_status, to_status, at, by, reason TEXT, approved_by NULL, pack_id NULL

প্রতিটি transition একটা সারি — reopen-এর reason বাধ্যতামূলক। অধ্যায় ৪৫-এর audit trail-এর প্রথম গ্রাহক।

Transition — নিয়ম কোডে

transition(period, to, by, reason = NULL): from = period.status allowed = { (future, open), (open, soft_closed), (soft_closed, open), (soft_closed, closed), (closed, open), -- reopen: approval (closed, locked) } যাচাই: (from, to) ∈ allowed; নইলে reject -- locked থেকে কিছু নয় যদি to == 'closed': যাচাই: prev(period).status ∈ (closed, locked) বা period_no == 0/1-এর প্রথম -- নিয়ম ১ যাচাই: reporting_packs WHERE period, status = 'final' আছে (period 0/13 ব্যতিক্রম) -- অধ্যায় ২৪ যদি to == 'open' AND from ∈ (soft_closed, closed): -- reopen যাচাই: সব later(period).status ∈ (open, future) -- নিয়ম ২ যাচাই: reason NOT NULL; approver ≠ by; permission 'period.reopen' (অধ্যায় ৪৮) final pack → superseded; snapshot(period …) is_stale = true যদি to == 'locked': যাচাই: from == closed; fiscal year-এর সব period closed (lock বছর-ধরে, মাস-ধরে নয়) UPDATE period.status; INSERT period_status_log

finalize(pack) (অধ্যায় ২৪) এই function-কে ডাকে to = closed — একই transaction।

post() — period-এর দরজা

অধ্যায় ৮-এর post()-এ যাচাই (এতদিন “period খোলা” বলে আসছি; এখন পুরো):

periodFor(company, posting_date, hint = NULL): p = accounting_periods WHERE start <= date <= end AND period_no BETWEEN 1 AND 12 যদি hint == 'adjustment': p = period 13 WHERE fiscal_year = FY(date) -- audit entry স্পষ্ট করে বলে যদি hint == 'opening': p = period 0 return p canPost(entry): p = periodFor(entry.posting_date, entry.period_hint) যদি p.status == 'open': ✓ যদি p.status == 'soft_closed': entry.source_type ∈ adjusting set বা entry.approved_for_soft_close → ✓; নইলে reject যদি p.status ∈ (closed, locked, future): reject ("period Dec 2025 closed; post in current period or request reopen") period 0: শুধু source_type = 'opening_balance'; period 13: শুধু adjusting set + closing_entry + audit_adjustment

Reject-এর বার্তায় কী করতে হবে — অধ্যায় ২৫-এর blocked_reason-এর রীতি।

closeYear() — একবার, idempotent, উল্টানো যায়

closeYear(company, fy, by): যাচাই: period 1..12 সব closed; period 13 open; বছরের pack (1–13) final dividend_declarations (fy) সব posted; কোনো draft entry নেই key = ('closing_entry', fy) যদি journal_entries WHERE idempotency_key = key: return (আগেই হয়েছে) bal = closing(company, fy.end) filter REVENUE, EXPENSE -- pre-closing, period 13 সহ lines = [] প্রতিটি a in bal WHERE bal[a] ≠ 0: lines += (a, debit = max(−bal[a].signed_dr, 0) …) -- credit balance → Dr, debit balance → Cr profit = Σ credit balances − Σ debit balances lines += (3300, profit > 0 ? credit : debit, |profit|) e1 = post(JV(period = 13, date = fy.end, source = closing_entry, key, lines)) -- ধাপ ১ e2 = post(JV(period = 13, date = fy.end, source = closing_entry, key = ('closing_transfer', fy), Dr 3300 / Cr 3200 |profit| (ক্ষতি উল্টো) + proprietorship: Dr 3100 / Cr 3400 (drawings balance))) -- ধাপ ২ transition(period 13, closed, by) rebuildSnapshot(company, FY next, period 1) -- opening = post-closing assert closing(fy.end)[REVENUE, EXPENSE, 3300] == 0 assert closing(fy.end)[3200] == আগের 3200 + profit

Line-এর সংখ্যা — সব 4xxx/5xxx এক entry-তে (৩০–২০০ line) — ledger-এ প্রতিটি account-এ একটা “closing” line, drill-down সহজ; অধ্যায় ২৫-এর exercise ৬-এর যুক্তি এখানেও।

reopenYear() — closing entry উল্টে

reopenYear(company, fy, by, reason): যাচাই: fy locked নয়; পরের FY-এর কোনো period closed নয় (নিয়ম ২ বছর-স্তরে); approval e1, e2 = entries WHERE key IN (('closing_entry', fy), ('closing_transfer', fy)) reverse(e2), reverse(e1) -- অধ্যায় ৪৩-এর reversal, period 13-এ, key ('reversal', e.id) transition(period 13, open); বছরের pack → superseded; snapshot next FY stale log

Reversal — নতুন entry, পুরনোটা থাকে (immutable, অধ্যায় ১৯)। আবার closeYear() চালালে নতুন key? না — একই key-তে আগের entry আছে (reversed)। Key-তে reversed_at IS NULL শর্ত, বা key-তে attempt নম্বর: ('closing_entry', fy, n)। দ্বিতীয়টা সরল।

movement() ও closing() — closing entry-র সাথে

অধ্যায় ২০-এর movement()-এ default:

movement(company, from, to, filter = {}, include_closing = false): WHERE … AND (:include_closing OR source_type NOT IN ('closing_entry')) AND (:include_adjustment_period OR period.period_no <> 13) -- মাসের report-এ 13 বাদ; বছরের-এ সহ (config) closing(company, as_of): movement(NULL, as_of, include_closing = true, include_adjustment_period = true)

দুটো flag; Balance Sheet সব সহ, Income Statement/SOCE-profit বাদ। currentYearProfit() (অধ্যায় ২১) movement() দিয়ে — closing-এর পরে স্বয়ংক্রিয় 0 নয়! closing_entry বাদ দিলে movement() পুরো বছরের লাভই দেখাবে। তাই:

currentYearProfit(company, as_of): যদি closing entry (FY(as_of)) posted: return 0 -- 3200-এ চলে গেছে নইলে: Σ movement(fy_start, as_of, REVENUE+EXPENSE).signed_cr

অধ্যায় ২১-এ এই শর্তের প্রতিশ্রুতি ছিল — এখানে তার কারণ: flag-এর জন্য closing entry movement()-এ অদৃশ্য, তাই “লাভ চলে গেছে” কথাটা সূত্র নিজে জানে না, বলে দিতে হয়।

Opening balance entry — প্রথম দিন

postOpeningBalances(company, fy, lines, by): যাচাই: company-র কোনো posted entry নেই (প্রথম দিন), বা migration mode (অধ্যায় ৬২) Σ debit == Σ credit (নইলে পার্থক্য 3200-এ নয় — কারণ খুঁজুন; অধ্যায় ৬২-এর suspense) REVENUE/EXPENSE line নেই (mid-year migration ব্যতিক্রম, অধ্যায় ৬২) period 0 (is_opening), date = fy.start − 1, source = opening_balance, key ('opening_balance', fy) control account-এর line-এ subledger detail বাধ্যতামূলক (A/R → open invoices, অধ্যায় ৬২) transition(period 0, closed) -- সাথে সাথে

Period তৈরি — বছর খোলা

openFiscalYear(company, fy): যাচাই: আগের FY-এর period 12 অন্তত soft_closed (দুটো বছর একসাথে খোলা — জুলাই-সেপ্টেম্বর — স্বাভাবিক) INSERT periods 0 (closed, খালি), 1..12 (1 open, বাকি future), 13 (future) fiscal_year নামকরণ অধ্যায় ১-এর convention

মাসের শুরুতে future → open — স্বয়ংক্রিয় (তারিখে) বা হাতে; একসাথে দুটোর বেশি মাস open থাকলে সতর্কতা (checklist পিছিয়ে)।

রাতের যাচাই

৯০. closed/locked period-এ posted_lines যার created_at > closed_at → ০টি (দরজা ভাঙেনি) ৯১. open period-এর আগে কোনো open period → সতর্কতা (ক্রম; ২টার বেশি open → সতর্কতা) ৯২. closed FY-এ: closing(fy.end)[REVENUE, EXPENSE, 3300] == 0; post-closing TB == next FY period 1 opening ৯৩. period 13-এ source_type ∉ (adjusting set, audit_adjustment, closing_entry) → ০টি; period 0-তে ≠ opening_balance → ০টি ৯৪. locked period-এর snapshot is_stale = true → ০টি (হলে bug — কেউ post করেছে) ৯৫. period_status_log-এ reopen যার reason খালি বা approved_by NULL → ০টি

Test হিসেবে

test "ক্রম: আগেরটা বন্ধ না হলে পরেরটা নয়": assert rejects transition(Dec, closed) while Nov.status == open test "reopen: পরেরটা বন্ধ থাকলে না": close(Nov); close(Dec) assert rejects transition(Nov, open) reopen(Dec, reason, approver); reopen(Nov, reason, approver) → ✓; Dec pack superseded test "soft_closed: adjusting হ্যাঁ, bill না": transition(Dec, soft_closed) assert rejects post(bill, Dec); assert post(accrual, Dec) test "closeYear idempotent, উল্টানো যায়": closeYear(2026); n = count(entries); closeYear(2026); assert count(entries) == n assert closing(30-06)[4110] == 0; closing(30-06)[3200] == 1,20,000 + 3,93,365 reopenYear(2026); assert closing(30-06)[4110] == 64,20,000 Cr; 3200 == 1,20,000 closeYear(2026); ✓ আবার (key attempt 2) test "closing entry জুনের Income Statement বদলায় না": is_before = incomeStatement(Jun 2026); closeYear(2026) assert incomeStatement(Jun 2026) == is_before assert incomeStatement(Jul 2026).line('Software Dev') == শুধু জুলাইয়ের test "Balance Sheet closing-এর আগে-পরে এক": bs1 = balanceSheet(30-06); closeYear(2026); bs2 = balanceSheet(30-06) assert bs1.total_equity == bs2.total_equity; bs2.line('3300') == 0; bs2.line('3200') == bs1.line('3200') + bs1.line('3300') test "period 13: মাসের pack অক্ষত, বছরের pack বদলায়": post(period 13, audit adjustment 1,25,000) assert incomeStatement(Jun 2026).net_profit অপরিবর্তিত assert incomeStatement(FY2026, include_adjustment_period).net_profit == 5,18,365 − 1,25,000 test "post-closing TB == পরের বছরের opening": closeYear(2026) assert trialBalance(FY2027, period 1).opening == closing(30-06-2026) প্রতিটি permanent account; 4xxx/5xxx opening 0 test "locked: কিছু নয়, কারো জন্য নয়": lock(FY2026) assert rejects transition(Jun 2026, open) even with 'period.reopen' permission assert rejects post(Jun 2026, anything) test "গত বছরের column closing-এর পরে ঠিক": closeYear(2026) assert incomeStatement(Dec 2026, columns = [this, last_year_same]).last_year.net_profit == 90,800

৬. Financial Statement Impact

Closing entry-র উদ্দেশ্যই “কোনো statement না বদলানো”:

Income Statement বদলায় না — movement() closing_entry বাদ; বছরের IS = 1–13, মাসের = নিজের period Balance Sheet মোট বদলায় না; equity-র ভিতরে 3300 → 3200 SOCE একটা row: "Transfer to retained earnings" — profit column −, RE column +, total 0 Cash Flow কিছু নয় — non_cash category (অধ্যায় ২২), দুটো কাটাকাটি

আর period lock-এর ছাপ প্রতিটি statement-এ: closed period-এর statement আর বদলায় নাstatement_runs.hash আবার চালালে মেলে (অধ্যায় ২০-এর যাচাই ৬১ — এই অধ্যায়ে তার প্রতিশ্রুতি পূরণ হলো)। Locked period-এর জন্য এটা চিরস্থায়ী; সেটাই “ছাপা statement”-এর মানে।


৭. Common Developer Mistakes

ভুলকী ঘটেসঠিক পথ
Closing entry ৩০ জুনে সাধারণ period-এজুনের Income Statement শূন্য/ঋণাত্মকperiod 13 + movement() বাদ
Closing entry ০১ জুলাইয়েজুলাইয়ের IS ঋণাত্মক; গত বছরের 3200 ভুল তারিখে৩০ জুন, period 13
movement()-এ closing বাদ, কিন্তু closing()-এওBalance Sheet-এ 3200 কখনো বাড়ে নাদুই flag; closing() সব সহ
currentYearProfit() closing-এর পরে বছরের লাভ দেখায়Balance Sheet-এ লাভ দুবার (3200 + 3300)closing posted হলে 0
Closing entry হাতে, প্রতি account আলাদা JV৩০টা entry, একটা বাদ, 3200 ভুলcloseYear(), এক entry, key
Closing entry দুবার3200 দ্বিগুণidempotency_key; reopen-এ attempt নম্বর
Reopen করলে closing entry মুছে ফেলাaudit trail নেইreversal, নতুন entry
Closed period-এ post “admin” দিয়েpack-এর সংখ্যা বাসি, hash ভাঙে, কেউ জানে নাদরজা post()-এ; admin-ও নয়; reopen procedure
Reopen পিছনের period, সামনেরটা closed রেখেসামনের opening বদলায়, ছাপা Balance Sheet মিথ্যানিয়ম ২
Audit adjustment জুনে reopen করে১২ মাসের pack superseded; জুলাই-আগস্ট নড়েperiod 13
Audit adjustment সেপ্টেম্বরেবছরের Balance Sheet ভুল ছাপাperiod 13, তারিখ ৩০ জুন
Period 13 সাধারণ document-এর জন্যও খোলা”জুনের bill” সেপ্টেম্বরে period 13-এ — ১২ মাসের pack আর বছরের pack মেলে না, কেউ জানে না কেনশুধু adjusting/audit; যাচাই ৯৩
Locked reopen-এর backdoorআইনি সমস্যাlocked = কখনো নয়; prior-period adjustment চলতি বছরে
OB entry FY-এর প্রথম দিনেবছরের movement()-এ OB ঢোকে; জুলাইয়ের IS-এ গত বছরের সবperiod 0, আগের দিন
OB-র অমিল 3200-এ plugmigration-এর ভুল retained earnings-এ চিরকালbalanced বাধ্যতামূলক; suspense + অধ্যায় ৬২
Snapshot rebuild না করে বছর খোলাজুলাইয়ের opening ০closeYear() → rebuildSnapshot(next, 1)
Period status শুধু UI-তে (API bypass)integration post করে closed-এpost()-এ; অধ্যায় ৪২

প্রথম আর অষ্টমটা মূল। Closing entry-র জায়গা ভুল হলে বছরের শেষ মাস চিরকাল অদ্ভুত দেখাবে, আর কেউ বুঝবে না কেন; closed period-এ “admin” post — এই একটা backdoor থাকলে অধ্যায় ২০–২৫-এর সব hash, pack, lock, status অর্থহীন। Period close হলো এই বইয়ের একমাত্র জায়গা যেখানে “না” মানে না — permission নয়, নীতি।


৮. Exercises

সেট ক — হাতে

১। রহমান ট্রেডার্স (অধ্যায় ২৩): ৩১-১২-২০২৫-এ closing entry — revenue 18,00,000 (দুটো account), expense 15,90,000 (পাঁচটা), drawings 2,20,000, capital 14,80,000 (adjustment-সহ)। দুই ধাপ লিখুন। post-closing TB-র 3100? ২। Section ৪-এর FY2026: যদি ২০-০৬-এ interim dividend 1,00,000 ঘোষণা হতো (অধ্যায় ২৩-এর exercise ১) — closing entry বদলায়? 3200 closing কত? distributable-এর যাচাই কোন সংখ্যায়? ৩। ক্ষতির বছর: revenue 40,00,000, expense 46,00,000। closing entry দুই ধাপ। 3200 আগে 1,20,000 — পরে? ঋণাত্মক retained earnings Balance Sheet-এ কীভাবে দেখাবেন? ৪। Period 13-এ audit adjustment 1,25,000 post হলো, তারপর closing। জুনের মাসিক IS, বছরের IS (1–12), বছরের IS (1–13), Balance Sheet ৩০-০৬ — চারটির লাভ/3300 কত? কোনটা ছাপা হয় কোথায়?

সেট খ — নিয়ম

৫। নিয়ম ২ (reopen)-এর একটা উদাহরণ সংখ্যায়: নভেম্বর reopen করে 10,000-এর bill post — ডিসেম্বরের Balance Sheet-এর কোন লাইন কত বদলাত, ডিসেম্বর closed থাকলে কে জানত? ৬। soft_closed-এ "approved_for_soft_close" — কোন document-এর দরকার হতে পারে (adjusting নয়, কিন্তু মাসের)? কে approve করে? এটা কি নিয়মের ফাঁক? ৭। দুটো বছর একসাথে open (জুলাই–সেপ্টেম্বর): FY2026 period 13 open, FY2027 period 1–3 open। কোন কোন report দুই বছরের data মেশাতে পারে? snapshot-এর is_stale কীভাবে ছড়ায় (period 13-এ post → FY2027 সব stale)? ৮। Locked-এর পরে ভুল: FY2026-এ 2,00,000-এর revenue দুবার post হয়েছিল, ধরা পড়ল FY2027-এর নভেম্বরে। কী করবেন (অধ্যায় ২৩)? FY2026-এর ছাপা statement? FY2027-এর comparative column?

সেট গ — কোনটা ভুল

৯। UPDATE accounting_periods SET status = 'open' WHERE id = … — "admin script" ১০। closing entry: প্রতি account আলাদা JV, source = 'manual', তারিখ ৩০-০৬ ১১। closeYear(): DELETE FROM posted_lines WHERE account_type IN (REVENUE, EXPENSE) AND fy = … ১২। OB entry তারিখ ০১-০৭, period 1 ১৩। period 13-এ জুনের দেরিতে আসা bill (document) post — "৩০ জুনের তো" ১৪। movement() সবসময় closing_entry বাদ — Balance Sheet-এও ১৫। reopen: closing entry-র status = 'cancelled'

সেট ঘ — নকশা

১৬। transition()-এর allowed set data-তে না কোডে? company-ধরে আলাদা হতে পারে (কারো soft_closed নেই)? নিয়ম ১, ২ কি কখনো config? ১৭। closeYear() ২০০ line-এর এক entry — অধ্যায় ৮-এর post() validation (balanced, period, account active) সব চলে; 3300-এর দরজা (অধ্যায় ২৩) source_type = closing_entry-কে চেনে। কিন্তু কেউ API দিয়ে source_type = closing_entry বলে মাঝ-বছরে JV করলে? closeYear()-এর বাইরে এই source কীভাবে নিষিদ্ধ? ১৮। Multi-company year-end: ৫ company, একই FY। closeYear() ৫ বার — একটায় ব্যর্থ (pack final নয়)। বাকিগুলো? group-এর consolidated (অধ্যায় ৬১) কোন period-এ? ১৯। Snapshot (account_period_balances) আর continuous ledger: ১০ বছর, ৫ লাখ line। closing(as_of) কত দ্রুত? period 1-এর opening snapshot থেকে + চলতি period raw — কোন query? OB entry-র জায়গা এতে কী প্রভাব? ২০। Period lock-এর "না মানে না" — কিন্তু database-এ super user আছে। অধ্যায় ৪২, ৪৫-এর আগাম: কী কী স্তরে আটকাবেন (app, trigger, hash, append-only log)? কোন স্তর ভাঙা যায়, কোনটা ধরা পড়ে?

উত্তর আছে Workbook-এর Answer Key, অধ্যায় ২৬-এ।


৯. Developer Challenge

PeriodService — period state machine, বছর খোলা-বন্ধ, closing entry, opening balance — Part 3-র শেষ টুকরো, আর Part 5-এর অধ্যায় ৪২-এর ভিত্তি।

যা যা নকশা করবেন:

১. accounting_periods (0–13, পাঁচ status, is_opening/is_adjustment), period_status_log; transition() — allowed set, নিয়ম ১ ও ২, pack-এর শর্ত, reopen-এ reason + approver, lock বছর-ধরে; finalize(pack)-এর সাথে এক transaction।

২. post()-এ periodFor() + canPost() — status-ধরে, period 0/13-এর source-সীমা, soft_closed-এ adjusting; reject-এর বার্তায় “কী করতে হবে”; API-তেও একই দরজা (exercise ১৭)।

৩. closeYear() — pre-closing balance থেকে এক entry, key, দুই ধাপ, proprietorship mode, snapshot rebuild, দুটো assert; reopenYear() — reversal, attempt key, supersede; openFiscalYear()

৪. movement()-এ দুই flag (closing_entry, adjustment period); closing() সব সহ; currentYearProfit() closing-এর পরে 0; অধ্যায় ২০–২৪-এর test আবার সব পাস — একটাও বদলাতে হবে না (হলে flag-এর default ভুল)।

৫. Period 13 — audit adjustment document (audit_adjustments: auditor ref, approved দুজন), বছরের pack (1–13) v2, মাসের pack অক্ষত; বছরের IS-এর note “audit adjustments”।

৬. postOpeningBalances() — period 0, balanced বাধ্যতামূলক, control account-এ subledger detail; অধ্যায় ৬২-র জন্য কী রাখলেন (mid-year, open items, FX)।

৭. রাতের যাচাই ৯০–৯৫; test — section ৫-এর দশটি, আর: (ক) দুটো user একসাথে closeYear() — একটাই entry; (খ) reopen → post → close → hash আলাদা, log-এ কারণ; (গ) ১০ বছরের data-য় closing(as_of) snapshot দিয়ে < ১ সেকেন্ড (exercise ১৯)।

২ আর ৪ নম্বরটাই আসল পরীক্ষা। দরজা — closed period-এ post করার কোনো পথ নেই, admin-ও নয়, API-ও নয়, script-ও নয় (database super user ছাড়া — সেটা অধ্যায় ৪৫-এর কাজ); আর flag — closing entry যোগ করে Part 3-র একটা test-ও না ভাঙলে Part 3-র engine সম্পূর্ণ। এই দুটো নিয়ে Volume 1 শেষ।


১০. Summary Card

Period

future → open → soft_closed → closed → locked; reopen (approved, reason, log) closed/soft_closed → open; locked কখনো নয় open: সব; soft_closed: adjusting; closed/locked: কিছু নয় — admin-ও নয় close N ⇐ N−1 closed; reopen N ⇐ N+1… সব open (closing = opening + movement — পিছনে বদলালে সামনে বদলায়)

Closing entry

কখন: audit-এর পরে, period 13, তারিখ FY শেষ, সবার শেষে; একবার (key); উল্টানো = reversal ধাপ ১: Dr প্রতিটি REVENUE, Cr প্রতিটি EXPENSE, Cr 3300 (নিট) ধাপ ২: Dr 3300 / Cr 3200 (+ Dr 3100 / Cr 3400) ফল: 4xxx, 5xxx, 3300 = 0; 3200 += লাভ; কোনো statement-এর সংখ্যা বদলায় না (SOCE transfer row ছাড়া) movement() closing_entry বাদ (IS); closing() সহ (BS); currentYearProfit() closing-এর পরে 0 pre-closing TB (auditor) · post-closing TB == পরের বছরের opening

Period 0 / 13

0: OB entry, FY শুরুর আগের দিন, balanced, শুধু opening_balance — প্রথম দিন / migration (অধ্যায় ৬২) 13: audit adjustment + closing entry, FY শেষ দিন; মাসের pack অক্ষত; বছরের pack (1–13) v2; document নয়

Opening

continuous ledger (এক সত্য) + snapshot (period 1 opening = গত post-closing) — carry-forward-এর গতি, continuous-এর সত্য

Developer checklist

□ periods 0–13, পাঁচ status; period_status_log; transition() নিয়ম ১, ২; lock বছর-ধরে □ post(): periodFor(), canPost() status-ধরে; period 0/13 source-সীমা; API-তেও □ closeYear(): এক entry, key, দুই ধাপ, proprietorship; reopenYear(): reversal, attempt key □ movement() দুই flag; closing() সব সহ; currentYearProfit() closing-এর পরে 0 □ audit_adjustments document; বছরের pack 1–13 □ postOpeningBalances(): period 0, balanced, subledger detail □ openFiscalYear(); rebuildSnapshot(next, 1) □ রাতে: ৯০–৯৫

Part 3 শেষ — Volume 1 শেষ

অধ্যায় ২০ থেকে ২৬ — সাতটি অধ্যায়, একটা Trial Balance থেকে চারটি statement, তারপর সেগুলোকে এক pack-এ, তার আগে adjusting entry-র checklist, তার পরে period-এর দরজা। পুরোটা একটা engine-এ: movement(from, to) আর closing(as_of) — দুটো function, চারটি layout, নয়টি cross-check, একটা snapshot, একটা hash, একটা lock।

আর পিছনে তাকালে — Volume 1: অধ্যায় ১-এর “Accounting কী” থেকে অধ্যায় ২৬-এর “period locked”। একটা software company, জুলাই ২০২৫ থেকে জুন ২০২৬, প্রতিটি লেনদেন একটা journal entry, প্রতিটি entry একটা ledger-এ, প্রতিটি মাস একটা Trial Balance, প্রতিটি Trial Balance চারটি statement, প্রতিটি statement-এর প্রতিটি সংখ্যার নিচে একটা তালিকা, আর প্রতিটি তালিকার প্রতিটি টাকার একটা document। এটাই accounting — আর এটাই Volume 1-এর প্রতিটি অধ্যায়ের Developer Challenge একসাথে: একটা GL, একটা posting rule, একটা report engine।

Part 3-র exit criterion (Projects ও Exit Criteria): একটা journal entry দেখে বলতে পারা — Income Statement, Balance Sheet, না Cash Flow, কোন লাইনে, কোন মাসে। অধ্যায় ২২-এর section ৬-এর তালিকাটা সেই পরীক্ষা।

Volume 2 — Accounting in Business Applications এখান থেকে শুরু: অধ্যায় ২৭-এ Inventory — এই বইয়ে এতদিন যা ছিল না: stock, COGS, valuation — আর একই engine-এ একটা নতুন subledger।