অধ্যায় ১২ — Bank Reconciliation
Volume 1 · Part 2 — Core Business Accounting · Chapter 12
পূর্বশর্ত: অধ্যায় ১১ (Cash ও Bank), অধ্যায় ৯ (General Ledger)
১. Learning Objective
এই অধ্যায় শেষে আপনি পারবেন:
Bank statement কেন আপনার ledger-এর আয়না — এবং কেন তার Dr/Cr উল্টো, তা ব্যাখ্যা করতে
খাতা আর statement-এর পার্থক্যের চার শ্রেণি চিনতে
কোন পার্থক্যে journal entry লাগে, কোনটায় শুধু অপেক্ষা — তা আলাদা করতে
দুই দিক থেকে Bank Reconciliation Statement তৈরি করতে ও মেলাতে
statement-এর line আর ledger-এর line মেলানো (matching) — 1:1, 1:many, many:1 — বুঝতে
না-মেলা item পরের মাসে বয়ে নেওয়া (carry forward) সামলাতে
statement import করার idempotent নকশা করতে
একটি auto-matching engine-এর নিয়ম ও তার সীমা নকশা করতে
reconciled line কেন তালাবদ্ধ, এবং সেটা software-এ কীভাবে প্রয়োগ হয় — তা বলতে
reconciliation কী কী ভুল ধরে, আর কী কী কখনো ধরে না — তা চিনতেসময়: পড়া ৫৫ মিনিট + অনুশীলন ৬০ মিনিট।
২. Concept Explanation
দুটো খাতা, একটাই টাকা
আপনার Prime Bank account-এর হিসাব দুই জায়গায় লেখা হয় — আপনার ledger-এ (1120), আর ব্যাংকের নিজের ledger-এ। দুটো খাতা দুজন আলাদা মানুষ লেখেন, আলাদা সময়ে, আলাদা তথ্য দেখে। তাই মাস শেষে দুটোতে একই সংখ্যা থাকা প্রায় অসম্ভব।
আপনার ledger (1120) ব্যাংকের statement
─────────────────── ─────────────────
আপনি যা জানেন ব্যাংক যা জানে
cheque লেখার দিন cheque কাটার দিন
জমা দেওয়ার দিন clear হওয়ার দিন
charge জানেন না charge কেটে নিয়েছে
সুদ জানেন না সুদ দিয়ে দিয়েছেBank Reconciliation হলো দুটো সংখ্যার মাঝের প্রতিটি টাকার হিসাব দেওয়া — কোনটা কেন আলাদা, আর কোনটা ঠিক করতে হবে। শেষে দুই দিক থেকে একটাই সংখ্যায় পৌঁছাতে হবে।
এটা অধ্যায় ১০-এর Trial Balance-এর পরে আপনার দ্বিতীয় সবচেয়ে গুরুত্বপূর্ণ যাচাই — আর একদিক থেকে বেশি শক্তিশালী। Trial Balance নিজের খাতাকে নিজের সাথে মেলায়; reconciliation খাতাকে বাইরের সত্যের সাথে মেলায়। অধ্যায় ১০-এ যে ছয়টা ভুল Trial Balance ধরতে পারে না বলেছিলাম, তার কয়েকটা — বাদ পড়া entry, দুবার লেখা entry, ভুল অঙ্ক — bank account-এর ক্ষেত্রে reconciliation ঠিকই ধরে ফেলে।
আয়না — statement-এর Dr/Cr উল্টো কেন
প্রথমবার bank statement পড়া developer-রা প্রায় সবাই একই জায়গায় হোঁচট খান: টাকা জমা দিলে statement-এ লেখা থাকে Cr, আর আপনার ledger-এ সেটা Dr।
কারণটা অধ্যায় ৩-এর প্রকার থেকেই আসে। আপনার কাছে ব্যাংকে রাখা টাকা একটা asset। ব্যাংকের কাছে আপনার টাকা একটা liability — সে আপনাকে ওটা ফেরত দিতে বাধ্য। Statement লেখা হয় ব্যাংকের খাতা থেকে, তাই:
ঘটনা আপনার ledger (asset) ব্যাংকের খাতা (liability)
───── ──────────────────── ─────────────────────────
টাকা জমা Dr (asset বাড়ল) Cr (ব্যাংকের দেনা বাড়ল)
টাকা তোলা Cr (asset কমল) Dr (ব্যাংকের দেনা কমল)
ব্যালেন্স Dr balance Cr balanceতাই নিয়মটা: statement-এর Cr = আপনার Dr, statement-এর Dr = আপনার Cr। Import করার সময় এই উল্টানোটা একবার, একটাই জায়গায় করবেন — নইলে পুরো matching উল্টো হয়ে যাবে।
অনেক ব্যাংক Dr/Cr-এর বদলে “Withdrawal / Deposit” লেখে — সেটা সহজ। এই অধ্যায়ে আমরা সেই ভাষাই ব্যবহার করব।
পার্থক্যের চার শ্রেণি
দুটো খাতার পার্থক্য যত রকমই দেখাক, শেষ পর্যন্ত চার শ্রেণিতে পড়ে। শ্রেণিটাই বলে দেয় কী করতে হবে:
শ্রেণি ১ — খাতায় আছে, ব্যাংকে এখনো নেই (timing)
outstanding cheque cheque লিখেছেন, এখনো ব্যাংক থেকে কাটেনি
deposit in transit cheque জমা দিয়েছেন, এখনো clear হয়নিখাতা ঠিক, ব্যাংক শুধু পিছিয়ে। কিছু করার নেই — অপেক্ষা। পরের মাসে মিলে যাবে।
শ্রেণি ২ — ব্যাংকে আছে, খাতায় এখনো নেই
bank charges ব্যাংক কেটে নিয়েছে, আপনি জানতেন না
interest credit ব্যাংক সুদ দিয়েছে
direct debit ঋণের কিস্তি, বীমা — ব্যাংক নিজে কেটেছে
direct credit গ্রাহক transfer করেছে, আপনাকে বলেনি
dishonoured cheque জমা দেওয়া cheque ফেরত গেছেব্যাংক ঠিক, খাতা পিছিয়ে। প্রতিটির জন্য journal entry লাগবে। Statement-ই এখানে source document।
শ্রেণি ৩ — ভুল, যেকোনো পাশে
আপনার ভুল ৫৪,০০০ এর জায়গায় ৪৫,০০০ লিখেছেন
একই receipt দুবার লিখেছেন
ভুল bank account-এ লিখেছেন
ব্যাংকের ভুল অন্য কারো cheque আপনার account থেকে কেটেছে
একই deposit দুবার credit করেছেআপনার ভুল হলে সংশোধনী entry; ব্যাংকের ভুল হলে ব্যাংককে জানান, খাতায় কিছু নয় — ব্যাংক ঠিক করলে statement-এ উল্টো line আসবে।
শ্রেণি ৪ — অজানা
statement-এ একটা ৪০,০০০ টাকার withdrawal, আপনি চেনেন নাএটা সবচেয়ে গুরুত্বপূর্ণ শ্রেণি, কারণ এখানেই জালিয়াতি ধরা পড়ে। কেউ একটা cheque জাল করেছে, বা কারো card তথ্য চুরি হয়েছে। এটা মেলাবেন না, “misc” বলে entry দেবেন না — অনুসন্ধান করুন। না জানা পর্যন্ত এটা unmatched থাকবে, আর reconciliation অসম্পূর্ণ থাকবে।
চার শ্রেণি এক নজরে:
| শ্রেণি | কে পিছিয়ে | কী করবেন | উদাহরণ |
|---|---|---|---|
| ১ timing | ব্যাংক | অপেক্ষা | outstanding cheque, deposit in transit |
| ২ bank-only | খাতা | journal entry | charge, সুদ, direct debit/credit, dishonour |
| ৩ ভুল | যেকোনো | সংশোধনী entry / ব্যাংককে জানান | অঙ্ক উল্টে যাওয়া, দুবার লেখা |
| ৪ অজানা | — | অনুসন্ধান, entry নয় | অচেনা withdrawal |
Reconciliation Statement — দুই দিক থেকে
প্রচলিত রূপটা দুই ভাগে। দুটোই একই সংখ্যায় পৌঁছাতে হবে — সেটাই প্রমাণ যে প্রতিটি পার্থক্যের হিসাব দেওয়া হয়েছে:
BANK RECONCILIATION STATEMENT — <তারিখ>
ব্যাংকের দিক থেকে
statement-এর closing balance B
(−) outstanding cheques শ্রেণি ১
(+) deposits in transit শ্রেণি ১
(±) ব্যাংকের ভুল শ্রেণি ৩
─────────────────────────────────────────────
= adjusted bank balance X
খাতার দিক থেকে
ledger-এর closing balance L
(+) ব্যাংক credit করেছে, খাতায় নেই শ্রেণি ২
(−) ব্যাংক debit করেছে, খাতায় নেই শ্রেণি ২
(±) আপনার ভুল শ্রেণি ৩
─────────────────────────────────────────────
= adjusted book balance X ← একই সংখ্যালক্ষ করুন কোন শ্রেণি কোন দিকে যায়:
শ্রেণি ১ (timing) → ব্যাংকের দিকে সমন্বয়, খাতায় entry নয়
শ্রেণি ২ (bank-only) → খাতার দিকে সমন্বয়, প্রতিটিতে entryAdjusted book balance-ই আসল সংখ্যা। Balance Sheet-এ এটাই যাবে — statement-এর সংখ্যা নয়। কারণ outstanding cheque-এর টাকা আপনার আর নেই, ব্যাংক যতই সেটা দেখাক।
Reconciliation আসলে matching
উপরের statement-টা ফলাফল। কাজটা হলো line ধরে ধরে মেলানো — ledger-এর প্রতিটি line-এর বিপরীতে statement-এর একটা line খোঁজা:
LEDGER (1120) STATEMENT
───────────── ─────────
05-08 Dr 5,75,000 ABC chq ◀──────▶ 06-08 Dep 5,75,000 CLG DEP
10-08 Cr 1,50,000 chq 00412 ◀── ✗ (নেই — outstanding)
28-08 Cr 3,50,000 salary ◀──────▶ 28-08 Wdl 3,50,000 SALARY TRF
(নেই — bank only) ✗ ──▶ 31-08 Wdl 575 MAINT CHGমেলানো সবসময় এক-এক নয়:
1 : 1 একটা cheque, একটা statement line সবচেয়ে সাধারণ
1 : many একটা deposit slip-এ তিনটা cheque জমা, ledger-এ তিন line,
ব্যাংক একটা line-এ 2,45,000 credit করল statement-এ এক
many : 1 ব্যাংক একটা withdrawal-কে দুই line-এ ledger-এ এক line,
দেখাল (মূল + charge) statement-এ দুইতাই matching-এর একক line নয়, group: এক পাশে কয়েকটা line, অন্য পাশে কয়েকটা line, দুই পাশের যোগফল সমান।
মেলানোর পরে যা থাকে:
ledger-এ unmatched → শ্রেণি ১ (timing) বা শ্রেণি ৩ (আপনার ভুল)
statement-এ unmatched → শ্রেণি ২ (entry দিন) বা শ্রেণি ৩/৪ (অনুসন্ধান)Carry forward — গত মাসের অমিল এ মাসে মেলে
জুলাইয়ে একটা cheque লিখেছিলেন, আগস্টে ব্যাংক থেকে কাটল। জুলাইয়ের reconciliation-এ সেটা outstanding ছিল; আগস্টের statement-এ তার line এল। আগস্টে মেলানোর সময় জুলাইয়ের unmatched ledger line-গুলোও প্রার্থী।
জুলাই reconciliation: outstanding #00398 40,000 ← unmatched থাকল
│
▼
আগস্ট statement: 03-08 CHQ 00398 40,000 ← এবার মিললএর মানে reconciliation একটা মাসের কাজ নয় — একটা চলমান অবস্থা। প্রতিটি ledger line হয় matched, নয় unmatched; মাস শুধু একটা কাটার রেখা যেখানে দাঁড়িয়ে হিসাব দেন।
এর একটা পরিণতি: প্রথম reconciliation শুরু করতে একটা opening লাগে — কোন তারিখে statement আর ledger শেষবার মিলেছিল, আর সেদিন কোন কোন item unmatched ছিল। নতুন software-এ migration করার সময় এটাই সবচেয়ে বেশি ভুল হয় (অধ্যায় ৬২)।
Reconciled মানে তালাবদ্ধ
একটা ledger line যখন statement-এর line-এর সাথে মিলে গেছে, তখন ব্যাংক নিজে সাক্ষী দিয়েছে যে ওই টাকা ওই দিনে ওভাবেই নড়েছে। এরপর ওই line-এর সাথে কিছু করা মানে সাক্ষ্যের সাথে বিরোধ।
অধ্যায় ৮-এ post করা entry এমনিতেই অপরিবর্তনীয়। কিন্তু reversal তো করা যায়। Reconciled line-এর reversal-এ বাড়তি সতর্কতা লাগে:
reversal চাইলেন একটা matched line-এর
→ আগে unmatch করতে হবে (কারণ লিখে)
→ তারপর reversal
→ reversal-এর নতুন line আবার unmatched হয়ে reconciliation-এ ফিরবেকেন এত কড়াকড়ি? কারণ reconciliation সম্পূর্ণ হওয়া মানে “এই তারিখ পর্যন্ত bank ঠিক” — একটা প্রতিবেদন যার উপর মানুষ ভরসা করে। কেউ পরে চুপচাপ একটা matched line উল্টে দিলে সেই প্রতিবেদনটা মিথ্যা হয়ে যায়, আর কেউ জানেও না।
কত ঘন ঘন
মাসে একবার ন্যূনতম — statement আসার পরে
সপ্তাহে একবার ভালো — ভুল ছোট থাকতেই ধরা পড়ে
প্রতিদিন bank feed থাকলে — matching প্রায় স্বয়ংক্রিয়সময়ের সাথে reconciliation-এর কষ্ট বাড়ে, কমে না। তিন মাসের জমে থাকা statement মেলানো তিনগুণ নয়, দশগুণ কঠিন — কারণ পুরনো unmatched item-এর কারণ মনে থাকে না।
যা ধরে, যা ধরে না
Reconciliation যা ধরে:
✓ bank account ছোঁয়া কোনো entry বাদ পড়েছে
✓ একই receipt/payment দুবার লেখা হয়েছে
✓ অঙ্ক ভুল (54,000 বনাম 45,000)
✓ ভুল bank account-এ লেখা হয়েছে (1120-র জায়গায় 1125)
✓ ব্যাংকের ভুল
✓ জালিয়াতি — এমন লেনদেন যা আপনি করেননি
✓ হারিয়ে যাওয়া cheque (মাসের পর মাস outstanding)Reconciliation যা কখনো ধরে না:
✗ bank পাশ ঠিক, অন্য পাশ ভুল account-এ
(Dr Office Rent-এর জায়গায় Dr Electricity — bank-এর 65,000 ঠিকই মিলবে)
✗ যে লেনদেনে bank/cash নেই (invoice, accrual, depreciation)
✗ ঠিক অঙ্ক, ঠিক bank, কিন্তু ভুল party
(A/R-এ করিমের জায়গায় রহিম — bank-এর দিক থেকে অভিন্ন)অধ্যায় ১০-এর সেই সতর্কতাই আবার: যাচাইটা যা দেখে, শুধু তাই ধরে। Reconciliation দেখে bank পাশ; অন্য পাশের জন্য অন্য যাচাই লাগে — subledger reconciliation (অধ্যায় ১৪, ১৬, ৪৪) আর তুলনামূলক report।
৩. Accounting Rule
মূল নিয়ম
adjusted bank balance == adjusted book balance
reconciliation সম্পূর্ণ ⟺ statement-এর প্রতিটি line-এর হিসাব আছে
(matched, অথবা entry দিয়ে matched)শ্রেণি অনুযায়ী কাজ
| item | শ্রেণি | ব্যাংকের দিকে | খাতার দিকে | entry? |
|---|---|---|---|---|
| outstanding cheque | ১ | (−) | না | |
| deposit in transit | ১ | (+) | না | |
| bank charge | ২ | (−) | Dr Bank Charges / Cr Bank | |
| interest credit | ২ | (+) | Dr Bank / Cr Interest Income | |
| direct debit (ঋণ, বীমা) | ২ | (−) | Dr Loan (বা Expense) / Cr Bank | |
| direct credit (গ্রাহক) | ২ | (+) | Dr Bank / Cr A/R (party) | |
| direct credit, অচেনা | ২/৪ | (+) | Dr Bank / Cr Unidentified Receipts | |
| dishonoured cheque | ২ | (−) | Dr A/R (party) / Cr Bank | |
| আপনার অঙ্কের ভুল | ৩ | (±) | সংশোধনী entry, পার্থক্যের অঙ্কে | |
| ব্যাংকের ভুল | ৩ | (±) | না — ব্যাংককে জানান | |
| অচেনা withdrawal | ৪ | না — অনুসন্ধান |
Balance Sheet-এ কোন সংখ্যা
adjusted book balance ✓
statement balance ✗ (outstanding cheque-এর টাকা আপনার নয়)তালা
matched line → reversal-এর আগে unmatch বাধ্যতামূলক, কারণ সহ
completed reconciliation → ওই তারিখের আগের কোনো line-এর match বদলানো যাবে নাঅলঙ্ঘনীয়
কখনো "পার্থক্যের অঙ্কে" একটা entry দিয়ে মিলিয়ে দেবেন না।
প্রতিটি পার্থক্যের একটা কারণ আছে; কারণটাই entry, পার্থক্যটা নয়।৪. Real Business Example
আগস্ট ২০২৫ — Prime Bank-এর statement এল
অধ্যায় ১১-এ 1120-এর ledger শেষ হয়েছিল ৮,৮৪,৮০০ টাকায়। সেপ্টেম্বরের প্রথম সপ্তাহে ব্যাংক statement পাঠাল:
PRIME BANK LTD — ACCOUNT STATEMENT
A/C 4471 (CA) 01-08-2025 to 31-08-2025
DATE PARTICULARS WITHDRAWAL DEPOSIT BALANCE
───── ─────────── ────────── ─────── ───────
01-08 BALANCE B/F 11,50,000
01-08 TRF TO CITY BANK SND 8890 3,00,000 8,50,000
03-08 CHQ 00398 40,000 8,10,000
06-08 CLG DEP CHQ 118823 ABC LTD 5,75,000 13,85,000
19-08 CLG DEP CHQ 220145 80,000 14,65,000
20-08 EFT IN — S. AKTER 1,20,000 15,85,000
21-08 CHQ RETURN 220145 INSUFF FUND 80,000 15,05,000
21-08 CHQ RETURN CHARGE 250 15,04,750
25-08 LOAN INSTL A/C 5501 25,000 14,79,750
28-08 SALARY TRF BULK 3,50,000 11,29,750
31-08 A/C MAINT CHARGE 575 11,29,175
31-08 INTEREST CREDIT 1,200 11,30,375
───── ───────── ────────
7,95,825 7,76,200
31-08 CLOSING BALANCE 11,30,375খাতা বলে ৮,৮৪,৮০০, ব্যাংক বলে ১১,৩০,৩৭৫। পার্থক্য ২,৪৫,৫৭৫। এই সংখ্যাটার নিজের কোনো অর্থ নেই — এটা কয়েকটা আলাদা কারণের যোগফল। এবার একটা একটা করে বের করি।
ধাপ ১ — Opening মেলান
আগে দেখুন মাসের শুরুতে দুটো খাতা কোথায় ছিল:
জুলাই শেষে ledger 11,10,000
statement B/F 11,50,000
পার্থক্য 40,000জুলাইয়ের reconciliation-এ একটা outstanding cheque ছিল — #00398, ৪০,০০০, ১৫ জুলাইয়ের ভাড়া (অধ্যায় ১০-এর PV-07-0004)। ব্যাংক তখনো কাটেনি, তাই ব্যাংকের সংখ্যা ৪০,০০০ বেশি। Opening মিলছে — পার্থক্যের পুরোটাই একটা চেনা কারণে।
এই ধাপটা বাদ দিলে পুরো reconciliation ভুল দিকে যাবে। অধ্যায় ১০-এর সেই একই যুক্তি: opening আগে, period পরে।
ধাপ ২ — line ধরে মেলান
Ledger-এর প্রতিটি line (জুলাইয়ের carry forward সহ) statement-এর সাথে:
LEDGER LINE STATEMENT LINE ফল
─────────── ────────────── ──
PV-07-0004 15-07 Cr 40,000 #00398 03-08 CHQ 00398 40,000 ✓ carry forward মিলল
CV-08-0001 01-08 Cr 3,00,000 01-08 TRF TO CITY 3,00,000 ✓
CV-08-0003 05-08 Dr 5,75,000 06-08 CLG DEP ABC 5,75,000 ✓ একদিন পরে clear
PV-08-0001 10-08 Cr 1,50,000 #00412 — ✗ outstanding
RV-08-0003 18-08 Dr 80,000 19-08 CLG DEP 80,000 ✓
JV-08-0005 22-08 Cr 80,200 21-08 CHQ RETURN 80,000 ┐
21-08 RETURN CHG 250 ┘ ✗ 80,200 ≠ 80,250
PV-08-0004 28-08 Cr 3,50,000 28-08 SALARY TRF 3,50,000 ✓
— 20-08 EFT IN 1,20,000 ✗ bank only
— 25-08 LOAN INSTL 25,000 ✗ bank only
— 31-08 MAINT CHG 575 ✗ bank only
— 31-08 INTEREST 1,200 ✗ bank onlyপাঁচটা মিলল, ছয়টা জায়গায় প্রশ্ন। এবার প্রতিটি প্রশ্নকে শ্রেণিতে ফেলুন:
#00412 1,50,000 খাতায় আছে, ব্যাংকে নেই শ্রেণি ১ → অপেক্ষা
80,200 বনাম 80,250 দুই পাশেই আছে, অঙ্ক আলাদা শ্রেণি ৩ → কার ভুল?
EFT IN 1,20,000 ব্যাংকে আছে, খাতায় নেই শ্রেণি ২ → কে পাঠাল?
LOAN 25,000 ব্যাংকে আছে, খাতায় নেই শ্রেণি ২ → entry
MAINT 575 ব্যাংকে আছে, খাতায় নেই শ্রেণি ২ → entry
INTEREST 1,200 ব্যাংকে আছে, খাতায় নেই শ্রেণি ২ → entryধাপ ৩ — প্রতিটি প্রশ্নের উত্তর
৮০,২০০ বনাম ৮০,২৫০ — ৫০ টাকার পার্থক্য। অধ্যায় ১১-এ মনে করিয়ে দিয়েছিলাম: ব্যাংক ফোনে “প্রায় ২০০” বলেছিল, খাতায় ২০০ লেখা হয়েছিল। আসল charge ২৫০। আপনার ভুল — ৫০ টাকার সংশোধনী entry লাগবে। এটা কীভাবে match হবে তা নিচে।
EFT IN ১,২০,০০০ — S. AKTER। A/R subledger খুঁজুন: সালমা আক্তার নামে একজন গ্রাহকের ঠিক ১,২০,০০০ টাকার একটা invoice খোলা আছে। নিশ্চিত হয়ে এটা তার পাওনার বিপরীতে receipt। নিশ্চিত না হলে? তাহলে A/R-এ নয় — 2165 Unidentified Receipts-এ, পরে চিহ্নিত হলে সরানো হবে। আন্দাজে কারো নামে টাকা বসাবেন না — ভুল গ্রাহকের পাওনা মুছে যাবে, আর আসল গ্রাহক তাগাদা পেতে থাকবেন।
LOAN INSTL ২৫,০০০। ঋণের schedule দেখুন: এই কিস্তিতে মূল ২০,০০০, সুদ ৫,০০০।
MAINT CHG ৫৭৫, INTEREST ১,২০০। সোজা — bank charge আর সুদ।
ধাপ ৪ — খাতায় entry
শ্রেণি ২ ও ৩-এর প্রতিটির জন্য entry। Posting date statement-এর তারিখ (আগস্টের period খোলা থাকলে); source হলো reconciliation নিজেই:
JV-08-0007 20-08 সালমা আক্তার — EFT receipt
Dr 1120 Bank — Prime 1,20,000
Cr 1130 A/R [সালমা আক্তার] 1,20,000
JV-08-0008 25-08 ঋণের কিস্তি, আগস্ট
Dr 2510 Bank Loan — Long Term 20,000
Dr 5510 Interest Expense 5,000
Cr 1120 Bank — Prime 25,000
JV-08-0009 31-08 Account maintenance charge
Dr 5280 Bank Charges 575
Cr 1120 Bank — Prime 575
JV-08-0010 31-08 Interest credit, আগস্ট
Dr 1120 Bank — Prime 1,200
Cr 4510 Interest Income 1,200
JV-08-0011 21-08 Cheque return charge — সংশোধনী (২০০ লেখা, আসল ২৫০)
Dr 5280 Bank Charges 50
Cr 1120 Bank — Prime 50শেষটা লক্ষ করুন — সংশোধনী entry-টা পার্থক্যের অঙ্কে (৫০), মূল entry-কে reverse করে নতুন করে নয়। কারণ মূল entry-র ৮০,০০০ অংশ সম্পূর্ণ ঠিক ছিল; শুধু charge-টা কম ছিল।
Entry-গুলো post হওয়ার পরে ledger:
আগের closing 8,84,800
+ JV-08-0007 EFT সালমা 1,20,000
− JV-08-0008 ঋণের কিস্তি 25,000
− JV-08-0009 maint charge 575
+ JV-08-0010 সুদ 1,200
− JV-08-0011 return charge সংশোধন 50
─────────
adjusted book balance 9,80,375ধাপ ৫ — নতুন entry গুলো match করুন
নতুন পাঁচটা line এখন statement-এর সাথে মিলবে:
JV-08-0007 Dr 1,20,000 ◀──▶ 20-08 EFT IN 1,20,000 ✓
JV-08-0008 Cr 25,000 ◀──▶ 25-08 LOAN INSTL 25,000 ✓
JV-08-0009 Cr 575 ◀──▶ 31-08 MAINT CHG 575 ✓
JV-08-0010 Dr 1,200 ◀──▶ 31-08 INTEREST 1,200 ✓
JV-08-0005 Cr 80,200 ┐ 21-08 CHQ RETURN 80,000 ┐
JV-08-0011 Cr 50 ┘ ◀──▶ 21-08 RETURN CHG 250 ┘ ✓ group: 80,250 = 80,250শেষটা একটা many : many group — ledger-এ দুই line, statement-এ দুই line, দুই পাশের যোগফল ৮০,২৫০। এক-এক করে মেলানো যেত না; group ছাড়া এই মামলা বন্ধ হতো না।
এখন statement-এর প্রতিটি line matched। শুধু ledger-এ একটা unmatched — #00412, যা স্বাভাবিক।
ধাপ ৬ — Reconciliation Statement
BANK RECONCILIATION STATEMENT
Prime Bank CA 4471 — ৩১ আগস্ট ২০২৫
ব্যাংকের দিক থেকে
Balance as per bank statement 11,30,375
(−) Cheques issued, not yet presented
#00412 10-08 PV-08-0001 1,50,000 (1,50,000)
(+) Deposits not yet credited —
────────────────────────────────────────────────────────────
Adjusted bank balance 9,80,375
খাতার দিক থেকে
Balance as per ledger (before adjustments) 8,84,800
(+) Credited by bank, not in ledger
EFT — সালমা আক্তার 1,20,000
Interest credit 1,200 1,21,200
(−) Debited by bank, not in ledger
Loan instalment 25,000
Account maintenance charge 575
Cheque return charge — short 50 (25,625)
────────────────────────────────────────────────────────────
Adjusted book balance 9,80,375 ✓দুই দিক থেকে একই সংখ্যা — ৯,৮০,৩৭৫। Reconciliation সম্পূর্ণ। Balance Sheet-এ Prime Bank দেখাবে ৯,৮০,৩৭৫, statement-এর ১১,৩০,৩৭৫ নয়।
সেপ্টেম্বরে carry forward:
unmatched ledger line: PV-08-0001 #00412 1,50,000 (outstanding since 10-08)সেপ্টেম্বরের statement-এ CHQ 00412 এলে এটা মিলবে। না এলে? তিন মাস পরেও না এলে সরবরাহকারীকে জিজ্ঞেস করুন — cheque হারিয়ে গেছে হয়তো। ছয় মাস পরে cheque-টা আইনত বাসি (stale); তখন উল্টে দিতে হবে (Dr Bank, Cr A/P) আর নতুন cheque দিতে হবে।
একটা জিনিস reconciliation ধরল না
সব মিলে গেল। কিন্তু অধ্যায় ১০-এর সেই বিদ্যুৎ বিলের মতো একটা ভুল এই মাসেও লুকিয়ে থাকতে পারত: ধরুন ২৮-০৮-এর ৩,৫০,০০০ বেতনটা ভুল করে Dr 5220 Office Rent দিয়ে লেখা হয়েছিল। Bank পাশ ৩,৫০,০০০ Cr — statement-এর সাথে নিখুঁত মিলবে। Reconciliation কিছুই বলবে না। এটা bank-এর যাচাই, খরচের নয়।
৫. Implementation — Software ও Database
Statement import — bank-এর line গুলো আপনার database-এ
সবার আগে statement-টা একটা table-এ আনতে হবে, ব্যাংক যে format-এই দিক (CSV, Excel, MT940, CAMT.053, API feed):
bank_statement_lines
id BIGINT PK
cash_bank_account_id BIGINT FK -- kind = bank
import_batch_id BIGINT FK
line_no INT -- statement-এ ক্রম
value_date DATE -- ব্যাংক যেদিন কার্যকর করেছে
description TEXT
reference VARCHAR(100) NULL -- cheque নম্বর, transfer ref (parse করা)
amount_signed DECIMAL(18,4) -- আপনার দৃষ্টিতে: deposit +, withdrawal −
balance_after DECIMAL(18,4) NULL -- ব্যাংকের running balance, থাকলে
line_hash CHAR(64) NOT NULL -- idempotency
match_status VARCHAR(20) -- unmatched | matched | excluded
match_group_id BIGINT FK NULL
UNIQUE (cash_bank_account_id, line_hash)
INDEX (cash_bank_account_id, value_date)
INDEX (cash_bank_account_id, match_status)তিনটি সিদ্ধান্ত এখানে:
১. amount_signed — আয়না উল্টানো একবারই। Import-এর সময় deposit-কে +, withdrawal-কে − করুন, আপনার দৃষ্টিতে। এরপর matching-এ ledger-এর debit - credit আর statement-এর amount_signed সরাসরি তুলনীয় — কোনো if ছাড়া।
২. line_hash — একই file দুবার import হলেও একই line দুবার ঢুকবে না।
line_hash = SHA256(value_date, amount_signed, description, balance_after, line_no)balance_after আর line_no থাকায় একই দিনে একই অঙ্কের দুটো আসল লেনদেন (দুটো ৫০০ টাকার charge) আলাদা থাকে, অথচ একই file আবার import করলে UNIQUE constraint চুপচাপ থামিয়ে দেয়। ON CONFLICT DO NOTHING — এটাই idempotent import (অধ্যায় ৪৬)।
৩. import_batch_id — কে, কখন, কোন file। ভুল file import হলে পুরো batch-টা (যদি কোনো line matched না হয়) মুছে ফেলা যায়।
bank_statement_imports
id BIGINT PK
cash_bank_account_id BIGINT FK
imported_by BIGINT
imported_at TIMESTAMP
source_file_name VARCHAR(255)
source_format VARCHAR(20) -- csv | mt940 | camt053 | api
period_from DATE
period_to DATE
opening_balance DECIMAL(18,4) -- statement যা বলেছে
closing_balance DECIMAL(18,4)
line_count INTImport-এর পরে একটা যাচাই: opening_balance + SUM(amount_signed) == closing_balance। না মিললে file-টা অসম্পূর্ণ বা parse ভুল — matching শুরুই করবেন না।
Match group — মেলানোর একক
Section ২-এ বলেছি matching-এর একক line নয়, group:
bank_recon_match_groups
id BIGINT PK
cash_bank_account_id BIGINT FK
reconciliation_id BIGINT FK NULL -- কোন reconciliation-এ বন্ধ হলো
match_type VARCHAR(10) -- auto | manual
matched_by BIGINT NULL
matched_at TIMESTAMP
ledger_total DECIMAL(18,4)
statement_total DECIMAL(18,4)
note TEXT NULL
CHECK (ledger_total = statement_total)bank_recon_match_ledger_lines bank_recon_match_statement_lines
match_group_id BIGINT FK match_group_id BIGINT FK
journal_line_id BIGINT FK statement_line_id BIGINT FK
UNIQUE (journal_line_id) UNIQUE (statement_line_id)দুটো UNIQUE গুরুত্বপূর্ণ — একটা line একটাই group-এ থাকতে পারে। একই cheque দুটো statement line-এর সাথে মেলানো database-ই ঠেকাবে।
CHECK (ledger_total = statement_total) — group তৈরির সময়েই যোগফল সমান হতে হবে। ৮০,২০০ বনাম ৮০,২৫০ group-টা ৫০ টাকার entry post হওয়ার আগে তৈরি করাই যাবে না। এটা ইচ্ছাকৃত: “প্রায় মিলছে” বলে কিছু নেই।
Ledger-এর দিকে journal_lines-এ দুটো column যোগ হয়:
journal_lines (+)
reconciled_at TIMESTAMP NULL -- match group-এ ঢোকার সময়
match_group_id BIGINT FK NULLএতে “1120-এর unmatched line” একটা সরল query: WHERE account_id = 1120 AND match_group_id IS NULL।
Reconciliation — একটা তারিখে দাঁড়িয়ে হিসাব
bank_reconciliations
id BIGINT PK
cash_bank_account_id BIGINT FK
as_of_date DATE
statement_balance DECIMAL(18,4) -- ব্যাংক যা বলেছে
ledger_balance DECIMAL(18,4) -- সব adjustment-এর পরে
unmatched_ledger_dr DECIMAL(18,4) -- deposits in transit
unmatched_ledger_cr DECIMAL(18,4) -- outstanding cheques
status VARCHAR(20) -- open | completed
completed_by BIGINT NULL
completed_at TIMESTAMP NULL
UNIQUE (cash_bank_account_id, as_of_date)সম্পূর্ণ (completed) হওয়ার শর্ত — দুটো:
১. as_of_date পর্যন্ত statement-এর কোনো unmatched line নেই
(ব্যাংকের প্রতিটি কথার হিসাব দেওয়া হয়েছে)
২. statement_balance
+ unmatched_ledger_dr (deposits in transit)
- unmatched_ledger_cr (outstanding cheques)
== ledger_balanceদ্বিতীয়টা section ৪-এর “দুই দিক থেকে একই সংখ্যা”-র সূত্র রূপ। প্রথম শর্ত না মানলে দ্বিতীয়টা কাকতালীয়ভাবে মিলে যেতে পারে — তাই দুটোই লাগে।
Unmatched ledger line থাকা reconciliation আটকায় না — সেটাই শ্রেণি ১। কিন্তু সেগুলো তালিকায় থাকবে, বয়সসহ।
Auto-matching engine
মানুষ line ধরে মেলাতে পারে, কিন্তু মাসে পাঁচশ লেনদেন হলে তা কেউ করবে না। Engine-এর কাজ নিশ্চিত জোড়াগুলো নিজে মেলানো, আর সন্দেহজনক গুলো মানুষের জন্য রেখে দেওয়া।
autoMatch(account, upto_date):
L = unmatched ledger lines (account, posting_date <= upto_date,
আগের মাসের carry forward সহ)
S = unmatched statement lines (account, value_date <= upto_date)
-- নিয়ম ১: reference + অঙ্ক (cheque নম্বর)
প্রতিটি s in S যার reference আছে:
c = L-এ যাদের instrument_no == s.reference
এবং signed == s.amount_signed
যদি |c| == 1: group(c, s, auto)
-- নিয়ম ২: অঙ্ক + তারিখের জানালা, একটাই প্রার্থী
প্রতিটি s in S (এখনো unmatched):
c = L-এ যাদের signed == s.amount_signed
এবং |posting_date - s.value_date| <= 5 দিন
যদি |c| == 1: group(c, s, auto)
যদি |c| > 1: রেখে দিন — মানুষ ঠিক করবে
-- নিয়ম ৩: এক statement line = কয়েকটা ledger line (deposit slip)
প্রতিটি s in S (এখনো unmatched, deposit):
c = L-এ Dr line, posting_date in [s.value_date - 5, s.value_date]
subset খুঁজুন যার যোগফল == s.amount_signed, আকার <= 5
যদি ঠিক একটা subset পাওয়া যায়: group(subset, s, auto)
-- বাকি সব unmatched: মানুষের জন্যতিনটি নীতি এই engine-এ:
নিশ্চিত না হলে মেলাবেন না
দুটো ৫০,০০০ টাকার cheque একই সপ্তাহে — কোনটা কোন line?
ভুল auto-match একটা না-মেলার চেয়ে অনেক খারাপ, কারণ
সেটা "মিলে গেছে" বলে লুকিয়ে থাকে।
নিয়মের ক্রম শক্ত থেকে নরম
reference-ভিত্তিক মিল সবচেয়ে নির্ভরযোগ্য, আগে চালান।
অঙ্ক-ভিত্তিক মিল দুর্বল, পরে।
subset খোঁজায় সীমা রাখুন
৫০টা line-এর মধ্যে যোগফল-মেলা subset খোঁজা exponential।
আকার ≤ ৫, তারিখের জানালা ছোট — নইলে engine ঝুলে যাবে,
আর কাকতালীয় মিল বাড়বে।মানুষের হাতে যা থাকে, তার জন্য একটা পর্দা: বাঁয়ে unmatched ledger, ডানে unmatched statement, দুই পাশে কয়েকটা বেছে “match” — যোগফল সমান না হলে button নিষ্ক্রিয়।
Bank-only line থেকে entry
শ্রেণি ২-এর item-এর জন্য reconciliation পর্দা থেকেই entry তৈরি হওয়া উচিত — statement line বেছে “post as…”:
postFromStatementLine(s, account_id, party = null):
entry = JV, posting_date = s.value_date (period খোলা থাকলে,
নইলে খোলা period-এর প্রথম দিন)
source_type = 'bank_statement_line', source_id = s.id
যদি s.amount_signed > 0:
Dr bank gl account s.amount_signed
Cr account_id (party) s.amount_signed
নইলে:
Dr account_id (party) -s.amount_signed
Cr bank gl account -s.amount_signed
post(entry)
group([entry-র bank line], [s], auto) -- সাথে সাথেই matchedকোন description-এ কোন account — এটা একটা নিয়মের table, কোডে if "CHARGE" in description নয়:
bank_statement_rules
cash_bank_account_id BIGINT FK NULL -- NULL = সব bank
pattern VARCHAR(200) -- 'MAINT CHARGE', 'INT CREDIT', 'LOAN INSTL%'
account_id BIGINT FK -- 5280, 4510, 2510 ...
party_type / party_id -- থাকলে
priority INTEngine নিয়ম মিললে প্রস্তাব করবে, নিজে post করবে না — মানুষ এক click-এ অনুমোদন দেবেন। ঋণের কিস্তির মূল/সুদ ভাগ, বা অচেনা EFT-র party — এগুলো নিয়মের table জানে না।
অচেনা receipt — 2165 Unidentified Receipts (LIABILITY)। এই account-এর ব্যালেন্স রাতের যাচাইয়ে থাকবে: ৩০ দিনের বেশি পুরনো line থাকলে সতর্কতা। কারণ যে টাকা কারো নামে নেই, সেই গ্রাহক তাগাদা পাচ্ছেন আর রাগ করছেন।
তালা — reconciled line-এ হাত দেওয়া
reverse(entry):
যদি entry-র কোনো line-এর match_group_id IS NOT NULL:
reject("line X reconciled on <date>; unmatch first, with reason")
unmatch(group, reason, by):
যদি group.reconciliation_id IS NOT NULL
এবং সেই reconciliation.status == 'completed':
reject("belongs to completed reconciliation <date>; reopen first")
group-এর সব line-এর match_group_id = NULL, reconciled_at = NULL
audit log: group, reason, by, at -- অধ্যায় ৪৫
group.status = 'unmatched' -- মুছবেন না, ইতিহাস
reopen(reconciliation, reason, by):
শুধু বিশেষ অনুমতিতে (অধ্যায় ৪৮)
status = 'open', audit logস্তরগুলো লক্ষ করুন — match, তার উপরে completed reconciliation, তার উপরে period lock (অধ্যায় ৪২)। প্রতিটি স্তর খুলতে আলাদা কারণ আর আলাদা অনুমতি।
Report ও যাচাই
Reconciliation Statement — section ৪-এর ছকটা bank_reconciliations + unmatched line থেকে সরাসরি তৈরি হয়; আলাদা করে সংরক্ষণের দরকার নেই, তবে completed হওয়ার মুহূর্তের একটা snapshot (PDF) রাখা ভালো — audit-এ চাইবে।
Unmatched items aging:
SELECT l.voucher_no, l.posting_date, l.debit, l.credit,
CURRENT_DATE - l.posting_date AS age_days
FROM posted_lines l
WHERE l.account_id = :bank_gl
AND l.match_group_id IS NULL
ORDER BY l.posting_date;রাতের স্বাস্থ্য-যাচাইয়ে:
১১. bank ledger-এ ৯০ দিনের বেশি পুরনো unmatched Cr line → সতর্কতা (হারানো cheque?)
১২. bank ledger-এ ৭ দিনের বেশি পুরনো unmatched Dr line → সতর্কতা (deposit clear হয়নি?)
১৩. statement-এ ৩০ দিনের বেশি পুরনো unmatched line → সতর্কতা (reconciliation পিছিয়ে)
১৪. 2165 Unidentified Receipts-এ ৩০ দিনের বেশি পুরনো line → সতর্কতা
১৫. শেষ completed reconciliation ৪৫ দিনের বেশি আগে → সতর্কতাTest হিসেবে
test "একই statement দুবার import করলে line দ্বিগুণ হয় না":
import(file)
n1 = count(statement_lines)
import(file)
n2 = count(statement_lines)
assert n1 == n2
test "reference মিললে auto-match হয়, দুটো প্রার্থী থাকলে হয় না":
payment(bank, cheque '00412', 1,50,000)
statement_line('CHQ 00412', -1,50,000)
autoMatch()
assert matched(ওই payment)
payment(bank, 50,000, date 10-08)
payment(bank, 50,000, date 12-08)
statement_line('WDL', -50,000, date 11-08)
autoMatch()
assert unmatched(দুটোই) -- মানুষের জন্য
test "group-এর যোগফল না মিললে তৈরি হয় না":
assert rejects group(ledger = [80,200], statement = [80,000, 250])
test "reconciled line reverse করা যায় না, unmatch ছাড়া":
match(line, statement_line)
assert rejects reverse(line.entry)
unmatch(group, reason = 'wrong match')
assert ok reverse(line.entry)
test "reconciliation সম্পূর্ণ হয় শুধু statement সব matched হলে":
... একটা statement line unmatched রেখে
assert rejects complete(reconciliation)
postFromStatementLine(সেই line, 5280)
assert ok complete(reconciliation)
assert reconciliation.statement_balance
+ unmatched_ledger_dr - unmatched_ledger_cr
== reconciliation.ledger_balance৬. Financial Statement Impact
Reconciliation নিজে কোনো statement-এ কিছু বদলায় না — এটা যাচাই। কিন্তু reconciliation থেকে জন্মানো entry গুলো বদলায়:
Income Statement
5280 Bank Charges +625 (575 + 50)
5510 Interest Expense +5,000
4510 Interest Income +1,200
Balance Sheet
1120 Bank — Prime 9,80,375 ← adjusted book, statement নয়
1130 A/R −1,20,000 (সালমার পাওনা মিটল)
2510 Bank Loan −20,000
2165 Unidentified Receipts (থাকলে — liability, কারণ কারো টাকা)সবচেয়ে গুরুত্বপূর্ণ: Balance Sheet-এ Bank-এর সংখ্যা adjusted book balance — ৯,৮০,৩৭৫। কেউ যদি “ব্যাংক তো ১১,৩০,৩৭৫ বলছে, সেটাই লিখি” বলেন — না। ওই ১,৫০,০০০ সরবরাহকারীর হাতে থাকা cheque-এ বাঁধা; ওটা আপনার টাকা নয়।
Reconciliation না করলে কী হয়? Balance Sheet-এ Bank দেখাবে ৮,৮৪,৮০০ — যার মধ্যে সালমার ১,২০,০০০ নেই (A/R ফুলে আছে), ঋণের কিস্তি নেই (Loan বেশি দেখাচ্ছে), সুদ নেই। প্রতিটি সংখ্যা একটু একটু ভুল, আর Trial Balance নিখুঁত মিলছে।
৭. Common Developer Mistakes
| ভুল | কী ঘটে | সঠিক পথ |
|---|---|---|
| Balance Sheet-এ statement balance | outstanding cheque-এর টাকা নিজের বলে দেখায় | adjusted book balance |
| পার্থক্যের অঙ্কে একটা “adjustment” entry | কারণ চিরতরে হারায়, জালিয়াতি লুকায় | প্রতিটি item-এর নিজের entry |
| Timing item-এর জন্য entry | পরের মাসে দুবার গোনা হয় | শ্রেণি ১-এ entry নয়, অপেক্ষা |
| শুধু অঙ্ক মিললেই auto-match | দুটো একই অঙ্কের line ভুল জোড়ায় | একটাই প্রার্থী হলে; নইলে মানুষ |
Import-এ line_hash নেই | একই file দুবার = সব line দ্বিগুণ | hash + UNIQUE + ON CONFLICT DO NOTHING |
| আয়না উল্টানো একাধিক জায়গায় | কোথাও ভুলে গেলে সব উল্টো | import-এ একবার, amount_signed |
| Matched line নিঃশব্দে reverse | completed reconciliation মিথ্যা হয়ে যায় | unmatch বাধ্যতামূলক, কারণ সহ |
| Opening না মিলিয়ে শুরু | গত মাসের অমিল এ মাসে ভুল ব্যাখ্যা পায় | আগে opening, তারপর line |
| Carry forward নেই | জুলাইয়ের cheque আগস্টে মেলানোর প্রার্থী নয় | সব unmatched line, মাস নির্বিশেষে |
| অচেনা receipt আন্দাজে গ্রাহকের নামে | ভুল গ্রাহকের পাওনা মুছে যায় | 2165 Unidentified, চিহ্নিত হলে সরান |
if "CHARGE" in description কোডে | নতুন ব্যাংক, নতুন ভাষা — কোড বদলাতে হয় | bank_statement_rules table |
| Subset-match-এ সীমা নেই | engine ঝুলে যায়, কাকতালীয় মিল বাড়ে | আকার ≤ ৫, তারিখের জানালা |
| ”প্রায় মিলছে” group | ৫০ টাকার পার্থক্য চাপা পড়ে | CHECK (ledger_total = statement_total) |
| Reconciliation-কে মাসিক কাজ ভাবা | তিন মাস জমলে কেউ আর করে না | চলমান অবস্থা; feed থাকলে দৈনিক |
প্রথম দুটো সবচেয়ে বিপজ্জনক, কারণ দুটোই তাড়াতাড়ি মিলিয়ে ফেলার প্রলোভন থেকে আসে। Reconciliation-এর মূল্য তার কষ্টেই — প্রতিটি পার্থক্যের কারণ খুঁজতে হয়। যে software সেই কষ্ট এড়ানোর একটা button দেয় (“auto-adjust difference”), সে reconciliation-কে নিরর্থক করে দেয়।
৮. Exercises
সেট ক — শ্রেণি চিনুন
প্রতিটির শ্রেণি (১–৪), কোন দিকে সমন্বয়, আর entry লাগবে কিনা:
১। ২৭ তারিখে সরবরাহকারীকে দেওয়া cheque, statement-এ নেই।
২। Statement-এ "SMS ALERT CHG 115", খাতায় নেই।
৩। ৩০ তারিখে জমা দেওয়া cheque, statement-এ নেই।
৪। Statement-এ ৩৫,০০০ টাকার একটা withdrawal, কেউ চেনে না।
৫। খাতায় ৭২,০০০, statement-এ ২৭,০০০ — একই cheque।
৬। Statement-এ গ্রাহকের ৯০,০০০ EFT, খাতায় নেই, গ্রাহক চেনা।
৭। Statement-এ গ্রাহকের ৯০,০০০ EFT, খাতায় নেই, কে পাঠাল জানা নেই।
৮। একই deposit statement-এ দুবার credit হয়েছে।
৯। খাতায় একই receipt দুবার লেখা, statement-এ একবার।
১০। জমা দেওয়া cheque ফেরত এসেছে, statement-এ দুই line (মূল + charge)।সেট খ — Reconciliation Statement তৈরি করুন
১১। City Bank (1125), ৩১ আগস্ট:
ledger closing 4,00,000
statement closing 4,12,600
statement-এ কিন্তু খাতায় নেই:
cheque book charge 400
interest credit 1,000
খাতায় কিন্তু statement-এ নেই:
৩০-০৮ cheque দেওয়া #10021, 12,000
দুই দিক থেকে reconciliation statement লিখুন। কোন entry লাগবে?
adjusted balance কত? Balance Sheet-এ City Bank কত দেখাবে?
১২। সেপ্টেম্বরের statement-এ #10021 এল — কিন্তু ২১,০০০ টাকায়।
সরবরাহকারীর bill দেখে নিশ্চিত হলেন cheque-টা আসলেই ২১,০০০-এর
ছিল; খাতায় ১২,০০০ লেখা হয়েছিল। (ক) এটা কোন শ্রেণি? (খ) পার্থক্য
৯,০০০ — অধ্যায় ১০-এর সূত্র কী বলে? (গ) কোন entry লাগবে?
(ঘ) সেপ্টেম্বরে match group-টা কেমন হবে — কোন কোন line?
(ঙ) আগস্টের completed reconciliation কি ভুল ছিল? কেন নয়?
১৩। একটা account-এ opening-ই মিলছে না: ledger 5,00,000,
statement B/F 5,60,000। জুলাইয়ের reconciliation-এ outstanding
cheque ছিল 35,000। বাকি 25,000 কোথায়? ধাপে ধাপে কী খুঁজবেন?সেট গ — Matching
১৪। Ledger unmatched: Dr 40,000 (05-08), Dr 40,000 (07-08),
Dr 25,000 (07-08)। Statement: Dep 40,000 (06-08), Dep 65,000 (08-08)।
auto-match engine কী করবে, ধাপে ধাপে? শেষে কী unmatched থাকবে?
১৫। Statement-এ একটা line 1,05,000 Dep। Ledger-এ ওই সপ্তাহে Dr line:
50,000, 30,000, 25,000, 55,000, 20,000। কয়টা subset যোগফলে
1,05,000 হয়? engine কী করা উচিত?
১৬। একটা matched cheque-এর reversal দরকার (সরবরাহকারী ভুল)।
কী কী ধাপ, কোন ক্রমে, আর প্রতিটিতে কে অনুমতি দেবেন?
১৭। একটা ledger line ৭ মাস ধরে unmatched Cr 18,000 (cheque)।
কী করবেন? entry লিখুন।সেট ঘ — নকশা
১৮। তিনটা ব্যাংকের তিন রকম CSV — column-এর নাম আলাদা, তারিখের
format আলাদা, একটা Dr/Cr column দেয়, একটা signed amount।
import layer-টা কীভাবে নকশা করবেন যেন নতুন ব্যাংক যোগ করতে
কোড না, config বদলাতে হয়?
১৯। line_hash-এ balance_after রাখলে একটা সমস্যা: ব্যাংক যদি
পুরনো line-এর পরে একটা back-dated line ঢোকায় (হয়), তার পরের
সব balance_after বদলে যায়। তখন কী হবে? বিকল্প কী?
২০। Period lock (অধ্যায় ৪২): আগস্ট বন্ধ, কিন্তু সেপ্টেম্বরে
আগস্টের statement এসে দেখা গেল একটা 575 charge post হয়নি।
posting_date কী হবে? reconciliation as_of_date কী হবে?
দুটো একই না হলে সূত্রটা কীভাবে মিলবে?
২১। Bank feed প্রতি ঘণ্টায় line পাঠায়। reconciliation "মাসিক
completed" ধারণাটা তখন কেমন হবে? দৈনিক completed? নাকি
"as_of" ছাড়া চলমান matched/unmatched-ই যথেষ্ট?উত্তর আছে Workbook-এর Answer Key, অধ্যায় ১২-এ।
৯. Developer Challenge
একটি BankReconciliationService নকশা করুন — import থেকে completed reconciliation পর্যন্ত।
যা যা নকশা করবেন:
১. Import pipeline — format-নিরপেক্ষ একটা normalized line (
value_date,amount_signed,description,reference), per-bank parser config,line_hashidempotency, আর import-পরবর্তীopening + Σ == closingযাচাই। একটা parser ভুল হলে (সব অঙ্ক উল্টো) আপনি কীভাবে টের পাবেন, batch মুছতে পারবেন কি?২. Match group model —
bank_recon_match_groupsও দুই পাশের line table,UNIQUEওCHECKসহ। many:many group তৈরির API কেমন? যোগফল না মিললে কী ফেরত দেবেন যাতে UI বলতে পারে “৫০ টাকা কম”?৩. Auto-match engine — অন্তত তিনটি নিয়ম, ক্রমসহ, আর প্রতিটির “একটাই প্রার্থী” শর্ত। subset-search-এর সীমা কী, আর সীমা ছাড়ালে কী করবেন? engine-এর ফলাফল কি সরাসরি group হবে, নাকি “প্রস্তাব” যা মানুষ অনুমোদন দেবেন? দুটোর trade-off লিখুন।
৪.
postFromStatementLine()—bank_statement_rulesদিয়ে account প্রস্তাব, party নির্ধারণ (চেনা হলে A/R, অচেনা হলে 2165), period বন্ধ থাকলে posting_date-এর নিয়ম, আর post-এর সাথে সাথে auto-match।৫. তালার তিন স্তর — match, completed reconciliation, period lock। প্রতিটি খুলতে কী লাগে (কারণ, অনুমতি, audit)?
reverse()কোন স্তরে আটকাবে?৬.
complete(reconciliation)— দুটো শর্ত (statement সব matched; সূত্র মেলে), ব্যর্থ হলে কোনটা কেন তা বলে দেওয়া, আর সফল হলে একটা অপরিবর্তনীয় snapshot।৭. Unmatched aging report ও রাতের পাঁচটি যাচাই (section ৫)। stale cheque (৬ মাস) স্বয়ংক্রিয়ভাবে reverse হবে, নাকি শুধু সতর্কতা? সিদ্ধান্ত নিন, যুক্তি লিখুন।
৮. Migration — একটা চলমান company-কে এই system-এ আনার দিন কোন কোন তথ্য লাগবে যাতে প্রথম reconciliation সম্ভব হয়? (ইঙ্গিত: শেষ reconciled তারিখ, সেদিনের statement balance, সেদিনের outstanding item-এর তালিকা।) এগুলো কোথায় রাখবেন?
৩ আর ৫ নম্বরটাই আসল পরীক্ষা। Auto-match engine-এ “নিশ্চিত না হলে মেলাবেন না” নীতিটা কোডে ফোটানো — প্রতিটি নিয়মে
|candidates| == 1— এটাই একটা ভালো engine আর একটা বিপজ্জনক engine-এর পার্থক্য। আর তালার স্তরগুলো হলো Part 5-এর পুরো architecture-এর (period lock, reversal, audit trail) প্রথম বাস্তব রূপ।
১০. Summary Card
আয়না
statement Cr / Deposit = আপনার Dr
statement Dr / Withdrawal = আপনার Cr
উল্টানো একবারই, import-এ → amount_signedচার শ্রেণি
১ timing খাতায় আছে, ব্যাংকে নেই অপেক্ষা outstanding chq, deposit in transit
২ bank-only ব্যাংকে আছে, খাতায় নেই entry charge, সুদ, direct dr/cr, dishonour
৩ ভুল যেকোনো পাশে সংশোধনী / জানান অঙ্ক উল্টে যাওয়া, দুবার
৪ অজানা — অনুসন্ধান অচেনা withdrawalReconciliation Statement
statement balance − outstanding chq + deposits in transit = X
ledger balance + bank credits − bank debits ± ভুল = X (একই)
Balance Sheet-এ → adjusted book balance, statement নয়Matching
একক = group (many : many), দুই পাশের যোগফল সমান
এক line, এক group — UNIQUE
carry forward: সব unmatched line প্রার্থী, মাস নির্বিশেষে
completed ⟺ statement সব matched এবং সূত্র মেলেAuto-match নীতি
reference + অঙ্ক → অঙ্ক + তারিখ → subset (≤ 5)
প্রতিটিতে: একটাই প্রার্থী হলে, নইলে মানুষ
নিশ্চিত না হলে মেলাবেন নাতালা
matched line → reversal-এর আগে unmatch, কারণ সহ
completed recon → reopen লাগে, বিশেষ অনুমতি
period lock → অধ্যায় ৪২যা ধরে না
bank পাশ ঠিক, অন্য পাশ ভুল account / ভুল party
bank ছোঁয়া নয় এমন লেনদেনDeveloper checklist
□ bank_statement_lines, amount_signed আপনার দৃষ্টিতে
□ line_hash + UNIQUE + ON CONFLICT DO NOTHING
□ import-এর পরে opening + Σ == closing
□ match group: many:many, CHECK(ledger_total = statement_total)
□ journal_lines.match_group_id, UNIQUE
□ auto-match: |candidates| == 1, নইলে মানুষ
□ subset-search-এ আকার ও তারিখের সীমা
□ bank_statement_rules table, কোডে string match নয়
□ অচেনা receipt 2165-এ, আন্দাজে party নয়
□ reverse() matched line-এ reject
□ unmatch কারণ সহ, audit log, group মুছবেন না
□ complete(): দুটো শর্ত, snapshot
□ unmatched aging: Cr > 90 দিন, Dr > 7 দিন
□ stale cheque ৬ মাসে reverse
□ migration: শেষ reconciled তারিখ + outstanding তালিকাপরবর্তী অধ্যায়
অধ্যায় ১৩ — Sales ও Revenue: Cash ও Bank-এ টাকা কীভাবে আসে-যায় আর সেটা কীভাবে যাচাই হয় — দুটোই এখন জানা। এবার টাকা আসার সবচেয়ে বড় কারণটায় যাব: বিক্রি। পরের অধ্যায়ে দেখব invoice কীভাবে journal entry হয়, VAT কোথায় যায়, আর সেই প্রশ্নটা যা অধ্যায় ১১-এ উঁকি দিয়েছিল — আয় কখন “হয়”: invoice-এর দিনে, delivery-র দিনে, নাকি টাকা পাওয়ার দিনে?