অধ্যায় ১৭ — Revenue ও Expense Recognition
Volume 1 · Part 2 — Core Business Accounting · Chapter 17
পূর্বশর্ত: অধ্যায় ১৩ (Sales ও Revenue — deferred revenue), অধ্যায় ১৫ (Purchase — Dr কোথায়, তিনটি তারিখ), অধ্যায় ১০ (Trial Balance — period)
১. Learning Objective
এই অধ্যায় শেষে আপনি পারবেন:
Accrual basis আর cash basis-এর পার্থক্য বলতে, আর একই মাসে দুটোয় লাভ কেন আলাদা — তা দেখাতে
Revenue recognition-এর নিয়ম পাঁচ ধাপে প্রয়োগ করতে: contract → obligation → দাম → ভাগ → recognize
Point-in-time আর over-time revenue আলাদা করতে, আর over-time মাপার তিনটি পদ্ধতি বলতে
Bundle-এর দাম obligation-ধরে ভাগ করতে (standalone selling price)
Contract position (billed বনাম earned) থেকে deferred (liability) না unbilled (asset) বের করতে
Matching principle দিয়ে খরচের মাস ঠিক করতে: সরাসরি, period cost, না asset
2×2 matrix-এর চারটি account চিনতে: 2180, 1170, 1150, 2135
Cut-off-এর নিয়ম প্রয়োগ করতে — মাসের শেষ দিনে কোন document কোন দিকে
Recognition schedule নকশা করতে — contract, obligation, milestone, মাসিক job
Revenue বনাম billing বনাম cash — তিনটি report তৈরি করতে, আর deferred rollforward মেলাতেসময়: পড়া ৫৫ মিনিট + অনুশীলন ৬০ মিনিট।
২. Concept Explanation
যে প্রশ্নটা বারবার পিছিয়েছি
Part 2-র প্রতিটি অধ্যায়ে একটা প্রশ্ন এসেছে, আর প্রতিবার “অধ্যায় ১৭-এ” বলে সরিয়ে রেখেছি:
অধ্যায় ১৩ invoice সেপ্টেম্বরে, সেবা অক্টোবর থেকে → আয় কোন মাসের? (deferred)
অধ্যায় ১৫ bill অক্টোবরে, internet সেপ্টেম্বরের → খরচ কোন মাসের? (posting_date)
অধ্যায় ১৬ payment নভেম্বরে, খরচ ছিল অক্টোবরে → payment খরচ নয়
অধ্যায় ১১ টাকা এল, invoice হয়নি → advance, আয় নয়এগুলো চারটে আলাদা নিয়ম নয়। একটাই নিয়ম, চার জায়গায় দেখা:
আয় হয় যখন আপনি আপনার কাজটা সম্পন্ন করেন। খরচ হয় যখন আপনি সুবিধাটা ভোগ করেন। Invoice, bill, টাকা — এগুলো ঘটনার document, ঘটনা নয়।
একে বলে accrual basis। আর এই অধ্যায় সেই নিয়মের পুরো রূপ, একটা তিন মাসের project দিয়ে যেখানে invoice, আয় আর টাকা — তিনটি তিন মাসে পড়ে।
Accrual বনাম cash — একই মাস, দুই লাভ
Cash basis: টাকা এলে আয়, টাকা গেলে খরচ। সহজ, ছোট দোকানের জন্য ঠিক, আর কিছু দেশে ছোট প্রতিষ্ঠানের tax-এর জন্য বৈধ।
Accrual basis: কাজ হলে আয়, সুবিধা ভোগ হলে খরচ। সব হিসাবমান (IFRS, GAAP) এটাই চায়, আর এই বইয়ের প্রতিটি entry এটাই।
একই নভেম্বর, দুই চশমায় (section ৪-এর project):
CASH BASIS ACCRUAL BASIS
আয় 2,40,000 1,20,000 ← ৪০% টাকা এল, কাজ ২০%
বেতন 1,20,000 1,20,000
software license 60,000 5,000 ← ১২ মাসের, ১ মাস ভোগ
──────── ────────
লাভ +60,000 −5,000কোনটা “সত্যি”? দুটোই — দুটো আলাদা প্রশ্নের উত্তর। Cash basis বলে “নগদ কী হলো” — সেটা Cash Flow Statement-এ (অধ্যায় ২২)। Accrual বলে “ব্যবসা কেমন চলল” — সেটা Income Statement-এ। একটা ledger, দুটো দৃষ্টি। কিন্তু ledger-টা accrual-এ লেখা হয়, cash view তা থেকে বের করা হয় — উল্টোটা সম্ভব নয়।
Developer-এর দৃষ্টিতে: cash basis হলো
paymentstable-এরGROUP BY month। Accrual হলো posting_date দিয়ে GL। আপনি যদি “মাসের আয়” বের করতেpaymentsjoin করেন — cash basis লিখছেন, না জেনে।
Revenue recognition — পাঁচ ধাপ
হিসাবমানের (IFRS 15) নিয়মটা পাঁচ ধাপে, আর প্রতিটি ধাপ software-এ একটা table:
১. Contract গ্রাহকের সাথে চুক্তি — কী দেবেন, কত পাবেন contracts
২. Obligation চুক্তিতে কয়টা আলাদা "দেওয়ার জিনিস" contract_obligations
৩. দাম মোট কত contracts.total
৪. ভাগ মোট দামটা obligation-ধরে ভাগ obligations.amount
৫. Recognize obligation যখন/যতটা সম্পন্ন, ততটা আয় recognition_scheduleধাপ ২ সবচেয়ে গুরুত্বপূর্ণ। একটা চুক্তি — “ERP module বানিয়ে দেব + এক বছর support” — দুটো obligation, কারণ দুটো আলাদা সময়ে সম্পন্ন হয়। এক করে দেখলে আয় ভুল মাসে যাবে।
Point in time বনাম over time
প্রতিটি obligation দুই রকমের একটা:
POINT IN TIME একটা মুহূর্তে সম্পন্ন — হস্তান্তরের দিনে পুরো আয়
পণ্য বিক্রি delivery-র দিনে
একটা training যেদিন হলো
perpetual license key দেওয়ার দিনে
OVER TIME সময় ধরে সম্পন্ন — যতটা হলো ততটা আয়
support/maintenance মাসে মাসে সমান (straight-line)
development project milestone বা কাজের অগ্রগতি অনুযায়ী
SaaS subscription মাসে মাসেOver time-এ “কতটা হলো” মাপার তিনটি পদ্ধতি:
straight_line সময় দিয়ে 1,20,000 / 12 মাস = 10,000 প্রতি মাস
milestone output দিয়ে design 20% গৃহীত → 20% আয়
percent_complete input দিয়ে খরচ হয়েছে 40% (of অনুমান) → 40% আয়Software company-তে তিনটিই লাগে: support-এ প্রথমটা, fixed-price project-এ দ্বিতীয়টা, time & material বা বড় project-এ তৃতীয়টা। Milestone-এ একটা কড়া শর্ত: গ্রাহকের গ্রহণ (acceptance)। “আমরা design শেষ করেছি” যথেষ্ট নয়; “গ্রাহক design গ্রহণ করেছেন, ২০-১১, sign-off আছে” — তবেই আয়। নইলে developer-এর আশাবাদ Income Statement-এ ঢোকে।
Bundle — দাম ভাগ করা
চুক্তির মোট দাম যদি obligation-ধরে আলাদা লেখা থাকে (development ৬,০০,০০০, support ১,২০,০০০), ভাগ সরল। কিন্তু “package ৭,২০,০০০” হলে?
standalone দাম (আলাদা বেচলে যা): development 6,00,000
support 1,50,000
────────
7,50,000
package 7,20,000 (৪% ছাড়)
ভাগ, আনুপাতিক: development 6,00,000 × 7,20,000/7,50,000 = 5,76,000
support 1,50,000 × 7,20,000/7,50,000 = 1,44,000কেন এই ঝামেলা? কারণ নইলে বিক্রেতা ছাড়টা পুরো support-এ চাপিয়ে দেবেন — development-এর আয় (এ বছর) বেশি, support-এর আয় (পরের বছর) কম। ভাগের নিয়ম manipulation আটকায়। Software-এ: item master-এ standalone_price, contract-এ allocation স্বয়ংক্রিয়, override-এ কারণ।
Contract position — deferred না unbilled
অধ্যায় ১৩-এ deferred revenue দেখেছি: invoice হলো, আয় হয়নি। এর আয়না: আয় হলো, invoice হয়নি — unbilled revenue (accrued revenue), একটা asset (গ্রাহকের কাছে পাওনা, শুধু invoice-টা এখনো লেখা হয়নি)।
দুটো একই contract-এর দুই অবস্থা, একটা সংখ্যা দিয়ে:
position = billed_to_date − recognized_to_date
position > 0 → বেশি bill, কম কাজ → 2180 Deferred Revenue (LIABILITY)
position < 0 → বেশি কাজ, কম bill → 1170 Unbilled Revenue (ASSET)
position = 0 → সমান billed ───────────────────────────────────────────▶
▲ ▲
│ 2,40,000 (নভ) │ 4,80,000 (জান)
│ │
earned ─────────────────────────────────────────────▶
▲ ▲ ▲
│ 1,20,000 │ 3,00,000 │ 1,80,000
position: নভ +1,20,000 deferred ডিসে −1,80,000 unbilled জান 0একটা contract একই সময়ে deferred আর unbilled দুটোই হয় না — নিট একটা। Contract-ধরে নিট, তারপর সব contract-এর ধনাত্মক যোগ = 2180, ঋণাত্মক যোগ = 1170। এটাই এই অধ্যায়ের প্রধান নকশা-সিদ্ধান্ত, আর section ৫-এ এটা দিয়েই অধ্যায় ১৩-এর isDeferred() সাধারণ হবে।
Expense recognition — matching
খরচের দিকে নিয়মটার নাম matching principle: খরচটা সেই মাসে, যে মাসের আয় সে তৈরি করতে সাহায্য করেছে। তিন রকম:
সরাসরি মেলে project-এর subcontractor, বিক্রি হওয়া মালের দাম (COGS)
→ যে মাসে সংশ্লিষ্ট আয়, সেই মাসে
মেলানো যায় না ভাড়া, admin বেতন, বিদ্যুৎ (period cost)
→ যে মাসে ভোগ, সেই মাসে
বহু মাসের সুবিধা license, বীমা, laptop, inventory
→ আগে asset, তারপর ভোগ অনুযায়ী খরচ
(prepaid → মাসে মাসে; fixed asset → অবচয়; inventory → COGS)লক্ষ করুন তৃতীয়টা অধ্যায় ১৫-এর “Dr কোথায়” — expense / asset / inventory — সেটা আসলে timing-এর সিদ্ধান্ত ছিল। Laptop asset কারণ তার সুবিধা ৩৬ মাসের; খরচ ৩৬ ভাগে।
একটা ফাঁদ: developer-এর বেতন কি project-এর আয়ের সাথে “match” করে পরের মাসে নেওয়া যায় (revenue ডিসেম্বরে, তাই বেতনও ডিসেম্বরে)? না। বেতন period cost — যে মাসে কাজ, সেই মাসে। Matching মানে খরচ পিছিয়ে দেওয়া নয়; খরচ যখন ভোগ হলো তখনই, আর আয় যখন হলো তখন। দুটো আলাদা মাসে পড়লে লাভ ওঠানামা করবে — সেটাই সত্যি।
2×2 — চারটি account
টাকা (বা document) আগে না পরে × আয় না খরচ — চারটি ঘর, চারটি account:
document/টাকা আগে, কাজ/ভোগ আগে,
কাজ/ভোগ পরে document/টাকা পরে
───────────────── ──────────────────── ────────────────────
আয় 2180 Deferred Revenue 1170 Unbilled Revenue
(LIABILITY — দিতে হবে সেবা) (ASSET — পাব টাকা)
অধ্যায় ১৩ এই অধ্যায়
খরচ 1150 Prepaid Expense 2135 Accrued Expenses
(ASSET — পাব সেবা) (LIABILITY — দিতে হবে টাকা)
অধ্যায় ১৮ অধ্যায় ১৮চারটির চরিত্র এক: সময়ের ফাঁক ধরে রাখার account। ফাঁক বন্ধ হলে (কাজ হলো / bill এল) account খালি হয়। মাসের শেষে এই চারটিতে যা থাকে, তা-ই adjusting entry-র বিষয় — অধ্যায় ১৮ ও ২৫। এই অধ্যায়ে উপরের সারি; নিচের সারির কাঠামো, mechanics পরের অধ্যায়ে।
Cut-off — মাসের শেষ দিন
৩০ নভেম্বর রাত ১২টা একটা দেয়াল। কোন ঘটনা কোন পাশে — নিয়ম একটাই, ঘটনার তারিখ, document-এর নয়:
invoice 30-11, delivery 02-12 → ডিসেম্বরের আয় (invoice আগে — deferred)
delivery 29-11, invoice 03-12 → নভেম্বরের আয় (কাজ আগে — unbilled)
টাকা 28-11, সেবা ডিসেম্বর → ডিসেম্বরের আয় (advance)
bill 02-12, সেবা নভেম্বর → নভেম্বরের খরচ (accrued — অধ্যায় ১৮)
bill 28-11, সেবা ডিসেম্বর → ডিসেম্বরের খরচ (prepaid)Cut-off ভুলের সবচেয়ে সাধারণ কারণ document-এর তারিখ posting_date হয়ে যাওয়া — অধ্যায় ১৫-এর তিন তারিখের কথা। আর সবচেয়ে সাধারণ ইচ্ছাকৃত ভুল: বছরের শেষ দিনে invoice লেখা যে কাজ জানুয়ারিতে হবে — “এ বছরের target”। Software-এ আটকানোর পথ: invoice line-এ service_from বা obligation_id বাধ্যতামূলক হলে system নিজেই deferred করে দেয়; target-এর জন্য invoice লিখলেও আয় বাড়ে না।
Materiality — কতটা কড়া
১,২০০ টাকার বার্ষিক domain renewal — ১২ মাসে ১০০ করে ভাগ করবেন? নিয়ম বলে হ্যাঁ; বাস্তবতা বলে না। Materiality: যে অঙ্ক report-এর সিদ্ধান্ত বদলায় না, তাকে সরল করা যায়। একটা নীতি, লিখিত: “২০,০০০-এর নিচে বহু-মাসের খরচ সরাসরি expense; উপরে prepaid।” অধ্যায় ১৫-এর capitalization threshold-এর মতো — সংখ্যাটা config-এ, নীতিটা document-এ।
৩. Accounting Rule
কখন
| আয় | খরচ | |
|---|---|---|
| নিয়ম | obligation সম্পন্ন হলে (point) / যতটা হলো (over time) | সুবিধা ভোগ হলে |
| নয় | invoice, টাকা, contract সই | bill, payment, order |
| প্রমাণ | delivery note, acceptance sign-off, সময় | bill, timesheet, ভোগের মাস |
Over-time পদ্ধতি
straight_line earned = amount × elapsed / total (মাস বা দিন)
milestone earned = Σ accepted milestone amount (acceptance বাধ্যতামূলক)
percent_complete earned = amount × cost_to_date / estimated_total_costBundle
obligation.amount = contract.total × standalone_price / Σ standalone_priceContract position
position = billed_to_date − recognized_to_date
> 0 → 2180 Deferred Revenue (Cr)
< 0 → 1170 Unbilled Revenue (Dr)
contract-ধরে নিট; দুটো একসাথে নয়Entry
| ঘটনা | Debit | Credit |
|---|---|---|
| Invoice, contract-এর (unbilled আছে) | A/R | Unbilled Revenue (যতটা আছে), Deferred Revenue (বাকি), VAT |
| Recognize (deferred আছে) | Deferred Revenue (যতটা আছে), Unbilled Revenue (বাকি) | Revenue |
| Prepaid → খরচ (অধ্যায় ১৮) | Expense | Prepaid Expense |
| খরচ, bill আসেনি (অধ্যায় ১৮) | Expense | Accrued Expenses |
Cut-off
ঘটনার তারিখ জেতে, document-এর নয়
recognition entry-র posting_date = মাসের শেষ দিনঅলঙ্ঘনীয়
Milestone revenue = গ্রাহকের acceptance, নিজের মূল্যায়ন নয়।৪. Real Business Example
নদী গ্রুপের project — নভেম্বর ২০২৫ থেকে ফেব্রুয়ারি ২০২৬
০১-১১ — চুক্তি সই
contract C-2025-07 — নদী গ্রুপ
obligation 1 ERP module development 6,00,000 milestone
design 20% 1,20,000 পরিকল্পনা 20-11
build 50% 3,00,000 পরিকল্পনা 20-12
go-live 30% 1,80,000 পরিকল্পনা 31-01
obligation 2 support, 12 মাস, 01-02 থেকে 1,20,000 straight_line (10,000/মাস)
────────
total 7,20,000 + VAT 15%
billing: সইয়ে development-এর 40% 2,40,000 + VAT
go-live-এ বাকি + support 4,80,000 + VATচুক্তি সই — কোনো entry নেই। কাজ হয়নি, invoice হয়নি, টাকা আসেনি। শুধু contracts table-এ একটা সারি আর recognition schedule (section ৫)।
০১-১১ — Invoice ১: ২,৪০,০০০ + VAT, due ২২-১১
SV-11-0001 (line → obligation 1)
position আগে: billed 0 − recognized 0 = 0 → unbilled নেই → পুরোটা deferred
Dr 1130 A/R [নদী গ্রুপ] 2,76,000
Cr 2180 Deferred Revenue 2,40,000
Cr 2140 VAT Payable 36,000
position পরে: 2,40,000 − 0 = +2,40,000 (deferred)Revenue শূন্য। অধ্যায় ১৩-এর deferred — কিন্তু এবার service_from নয়, obligation_id বলছে এটা কোন কাজের অংশ।
২০-১১ — design গৃহীত: গ্রাহকের sign-off email, attachment-এ
milestone 'design' accepted_at = 20-11, accepted_by = [নদী গ্রুপের PM], doc_url = …Entry এখনই নয় — মাসের শেষে recognition job। (চাইলে acceptance-এর দিনেই; নীতি। এই বইয়ে মাসের শেষে, এক entry-তে।)
২৪-১১ — টাকা এল ২,৭৬,০০০
RV-11-0006
Dr 1120 Bank — Prime 2,76,000
Cr 1130 A/R [নদী গ্রুপ] 2,76,000আয় শূন্যই — টাকা আয় নয় (অধ্যায় ১৩, আবার)।
৩০-১১ — Recognition, নভেম্বর
earned to date: obligation 1 — accepted milestones = design 1,20,000
obligation 2 — শুরু 01-02, elapsed 0 = 0
recognized to date: 0
এ মাসে: 1,20,000
position আগে: +2,40,000 deferred → পুরো 1,20,000 deferred থেকে
JV-11-0009 posting_date 30-11, source = recognition
Dr 2180 Deferred Revenue 1,20,000
Cr 4110 Software Dev. Income 1,20,000
position পরে: 2,40,000 − 1,20,000 = +1,20,000 (deferred)নভেম্বর: আয় ১,২০,০০০ · billing ২,৪০,০০০ · cash ২,৭৬,০০০ (VAT সহ) — তিনটি সংখ্যা, তিনটি গল্প।
২২-১২ — build গৃহীত; ৩১-১২ recognition
earned to date: 1,20,000 + 3,00,000 = 4,20,000
recognized: 1,20,000
এ মাসে: 3,00,000
position আগে: +1,20,000 deferred
→ deferred থেকে 1,20,000, বাকি 1,80,000 unbilled
JV-12-0014 posting_date 31-12
Dr 2180 Deferred Revenue 1,20,000
Dr 1170 Unbilled Revenue 1,80,000
Cr 4110 Software Dev. Income 3,00,000
position পরে: 2,40,000 − 4,20,000 = −1,80,000 (unbilled)এই entry-টাই অধ্যায়ের কেন্দ্র। একই contract, একই entry-তে deferred খালি হলো আর unbilled জন্মাল। Balance Sheet-এ 2180-তে নদী গ্রুপের কিছু নেই, 1170-এ ১,৮০,০০০ — গ্রাহকের কাছে পাওনা, invoice ছাড়া।
ডিসেম্বর: আয় ৩,০০,০০০ · billing ০ · cash ০।
৩১-০১ — go-live গৃহীত; recognition; তারপর Invoice ২
recognition (আগে):
earned to date: 6,00,000; recognized 4,20,000; এ মাসে 1,80,000
position আগে: −1,80,000 → deferred নেই → পুরোটা unbilled
JV-01-0011 posting_date 31-01
Dr 1170 Unbilled Revenue 1,80,000
Cr 4110 Software Dev. Income 1,80,000
position: 2,40,000 − 6,00,000 = −3,60,000 (unbilled)
invoice (পরে): 4,80,000 net (development বাকি 3,60,000 + support 1,20,000)
unbilled আছে 3,60,000 → আগে সেটা খালি; বাকি 1,20,000 deferred
SV-01-0018 (line 1 → obligation 1: 3,60,000; line 2 → obligation 2: 1,20,000)
Dr 1130 A/R [নদী গ্রুপ] 5,52,000
Cr 1170 Unbilled Revenue 3,60,000
Cr 2180 Deferred Revenue 1,20,000
Cr 2140 VAT Payable 72,000
position পরে: 7,20,000 − 6,00,000 = +1,20,000 (deferred — support-এর)Invoice ২-এ Revenue স্পর্শ হয়নি — কারণ development-এর আয় ডিসেম্বর-জানুয়ারিতে হয়ে গেছে, আর support-এর আয় ফেব্রুয়ারি থেকে হবে। Invoice শুধু unbilled asset-কে A/R asset-এ বদলাল (এক asset থেকে আরেক asset), আর support-এর টাকাটা deferred-এ রাখল।
জানুয়ারি: আয় ১,৮০,০০০ · billing ৪,৮০,০০০ · cash ০।
২১-০২ — টাকা ৫,৫২,০০০; ২৮-০২ recognition
RV-02-0004
Dr 1120 Bank 5,52,000
Cr 1130 A/R [নদী গ্রুপ] 5,52,000
JV-02-0016 posting_date 28-02
earned: obligation 2, straight_line, 1 of 12 মাস = 10,000
Dr 2180 Deferred Revenue 10,000
Cr 4120 Maintenance & Support Income 10,000
position: 7,20,000 − 6,10,000 = +1,10,000 (deferred, ১১ মাসের support)ফেব্রুয়ারি: আয় ১০,০০০ · billing ০ · cash ৫,৫২,০০০।
তিনটি সংখ্যা, চার মাস
নভ ডিসে জান ফেব মোট
───────────── ──── ──── ──── ──── ────
Revenue 1,20,000 3,00,000 1,80,000 10,000 6,10,000
Billing (net) 2,40,000 0 4,80,000 0 7,20,000
Cash (gross) 2,76,000 0 0 5,52,000 8,28,000
2180 (মাস শেষে) 1,20,000 0 0 1,10,000
1170 (মাস শেষে) 0 1,80,000 0 0চারটি মাসের কোনোটাতেই তিনটি সংখ্যা এক নয়। Cash basis-এ এই company নভেম্বরে দারুণ, ডিসেম্বর-জানুয়ারিতে শূন্য, ফেব্রুয়ারিতে বিশাল। Accrual-এ কাজের ছন্দ: ছোট, বড়, মাঝারি, স্থির। দ্বিতীয়টাই ব্যবসা।
খরচের দিক — একই project
developer বেতন, ২ জন 1,20,000/মাস নভ, ডিসে, জান — যে মাসে কাজ period cost
dev tool license, বার্ষিক 60,000, 01-11 1150 Prepaid → 5,000/মাস, ১২ মাস অধ্যায় ১৮
UI subcontractor 40,000 কাজ ডিসেম্বরে, bill এল 10-01
→ ডিসেম্বরের খরচ, 2135 Accrued অধ্যায় ১৮
cloud, project-এর 15,000/মাস যে মাসে ব্যবহার period costProject-এর লাভ, accrual-এ:
নভ ডিসে জান
Revenue 1,20,000 3,00,000 1,80,000
বেতন (1,20,000) (1,20,000) (1,20,000)
license (5,000) (5,000) (5,000)
subcontractor — (40,000) —
cloud (15,000) (15,000) (15,000)
──────── ──────── ────────
লাভ (20,000) 1,20,000 40,000 মোট 1,40,000আর cash-এ (VAT বাদে): নভেম্বর +৪৫,০০০ (২,৪০,০০০ − ১,২০,০০০ − ৬০,০০০ − ১৫,০০০), ডিসেম্বর −১,৩৫,০০০, জানুয়ারি −১,৭৫,০০০ (subcontractor-এর ৪০,০০০ এ মাসে গেল), ফেব্রুয়ারি +৪,৮০,০০০। Manager cash-এর ছবি দেখলে ডিসেম্বরে project বন্ধ করে দিতেন — যে মাসে আসলে সবচেয়ে বেশি লাভ হলো।
Deferred revenue rollforward — মেলানোর সূত্র
2180 — নভেম্বর ২০২৫
opening 1,10,000 (করিম, অধ্যায় ১৩: 1,20,000 − অক্টোবরের 10,000)
+ billed, deferred অংশ 2,40,000 (SV-11-0001)
− recognized 1,30,000 (নদী 1,20,000 + করিম 10,000)
─────────
closing 2,20,000 == GL 2180 ✓প্রতিটি মাসে এই চারটি সংখ্যা; শেষটা GL-এর সাথে মিলতেই হবে। Auditor-এর প্রিয় প্রশ্ন: “deferred revenue কোন contract-গুলোর, আর কবে recognize হবে?” — উত্তরটা contract-ধরে position-এর তালিকা।
৫. Implementation — Software ও Database
Schema — পাঁচ ধাপ, চার table
contracts
id, company_id
contract_no VARCHAR(40) -- C-2025-07
customer_id BIGINT FK
signed_date DATE
total_amount DECIMAL(18,4) -- net of VAT
currency CHAR(3)
status VARCHAR(20) -- draft | active | completed | cancelled
billed_to_date DECIMAL(18,4) NOT NULL DEFAULT 0 -- সংরক্ষিত, রাতে পুনর্গণনা
recognized_to_date DECIMAL(18,4) NOT NULL DEFAULT 0 -- সংরক্ষিত
document_url TEXT
UNIQUE (company_id, contract_no)contract_obligations
id, contract_id, line_no
description TEXT
method VARCHAR(20) -- point_in_time | milestone
-- | straight_line | percent_complete
standalone_price DECIMAL(18,4) -- ভাগের জন্য
amount DECIMAL(18,4) -- ভাগ করা দাম — এটাই recognize হবে
revenue_account_id BIGINT FK -- 4110 / 4120 / …
service_from, service_to DATE NULL -- straight_line
delivered_at DATE NULL -- point_in_time
estimated_cost DECIMAL(18,4) NULL -- percent_complete
recognized_to_date DECIMAL(18,4) NOT NULL DEFAULT 0
CHECK (recognized_to_date <= amount)contract_milestones
id, obligation_id, seq
name VARCHAR(100)
amount DECIMAL(18,4) -- বা pct × obligation.amount
planned_date DATE
accepted_at DATE NULL
accepted_by VARCHAR(200) NULL -- গ্রাহকের পক্ষে কে
acceptance_doc_url TEXT NULL
CHECK (accepted_at IS NULL OR acceptance_doc_url IS NOT NULL) -- প্রমাণ ছাড়া acceptance নয়recognition_schedule
id, obligation_id
period CHAR(7) -- 2025-11
planned_amount DECIMAL(18,4) -- forecast: straight_line / planned_date থেকে
recognized_amount DECIMAL(18,4) NOT NULL DEFAULT 0
journal_entry_id BIGINT FK NULL
UNIQUE (obligation_id, period)আর sales_invoice_lines-এ একটা column:
sales_invoice_lines (+)
obligation_id BIGINT FK NULL -- থাকলে contract billing: position দিয়ে accountঅধ্যায় ১৩-এর service_from / service_to থাকল — contract ছাড়া সরল ক্ষেত্রের জন্য। obligation_id থাকলে সেটা জেতে।
Contract position
position(contract) = contract.billed_to_date − contract.recognized_to_date
deferredBalance(c) = max(position(c), 0)
unbilledBalance(c) = max(−position(c), 0)দুটো সংরক্ষিত column, প্রতিটি invoice ও recognition-এর একই transaction-এ update; রাতে invoice line আর schedule থেকে পুনর্গণনা।
Invoice — অধ্যায় ১৩-এর isDeferred() সাধারণ
অধ্যায় ১৩-এর issueInvoice()-এ credit পাশ ছিল: revenue বা deferred। এখন তিন সম্ভাবনা, contract position থেকে:
creditSideForLine(line, inv):
যদি line.obligation_id IS NULL:
-- অধ্যায় ১৩-এর নিয়ম
return isDeferred(line, inv.invoice_date)
? [(2180, line.net)]
: [(line.revenue_account, line.net)]
c = line.obligation.contract
unb = unbilledBalance(c)
a = min(line.net, unb) -- আগে unbilled খালি
b = line.net − a -- বাকি deferred
c.billed_to_date += line.net
return [(1170, a), (2180, b)] (শূন্য বাদ)Contract-এর invoice কখনো সরাসরি revenue-তে যায় না — revenue শুধু recognition job থেকে। এটা ইচ্ছাকৃত: আয়ের একটাই দরজা, তাই cut-off manipulation-এর জায়গা নেই — বছরের শেষ দিনে invoice লিখলে A/R বাড়ে, deferred বাড়ে, আয় নয়।
Lock: contract সারি FOR UPDATE — কারণ একই মুহূর্তে recognition job আর invoice দুটোই position পড়ছে (অধ্যায় ৪৭)।
Earned to date — পদ্ধতি অনুযায়ী
earnedToDate(ob, as_of):
point_in_time: ob.delivered_at ≤ as_of ? ob.amount : 0
milestone: Σ m.amount WHERE m.obligation_id = ob.id
AND m.accepted_at ≤ as_of
straight_line: যদি as_of < ob.service_from: 0
months_total = months(ob.service_from, ob.service_to)
months_elapsed = min(months(ob.service_from, as_of),
months_total)
-- rounding: শেষ মাস বাকিটা নেয়, যাতে Σ == amount
months_elapsed == months_total ? ob.amount
: round(ob.amount × months_elapsed / months_total)
percent_complete: cost = costToDate(ob, as_of) -- project ledger, অধ্যায় ৩৫
pct = min(cost / ob.estimated_cost, 1)
round(ob.amount × pct)Straight-line-এ দিন না মাস — config। ১,০০,০০০/১২ = ৮,৩৩৩.৩৩ — round করলে ১১ মাসে ৯১,৬৬৩, শেষ মাসে ৮,৩৩৭। Σ schedule == amount রাতের যাচাই; নইলে ৪ টাকা চিরকাল deferred-এ পড়ে থাকে।
Recognition job — মাসের শেষে
recognizeRevenue(period, by):
period_end = last day of period
যাচাই: period খোলা (অধ্যায় ২৬)
প্রতিটি obligation ob WHERE contract.status = 'active':
earned = earnedToDate(ob, period_end)
amt = earned − ob.recognized_to_date
যদি amt ≤ 0: continue -- ঋণাত্মক হলে? নিচে
c = ob.contract (lock)
def = deferredBalance(c)
fromDeferred = min(amt, def)
fromUnbilled = amt − fromDeferred
entry = JV(posting_date = period_end, source_type = 'recognition',
source_id = ob.id, narration = "C-… ob n, period")
Dr 2180 fromDeferred (থাকলে)
Dr 1170 fromUnbilled (থাকলে)
Cr ob.revenue_account amt
post(entry)
ob.recognized_to_date += amt; c.recognized_to_date += amt
UPSERT recognition_schedule (ob, period, recognized_amount = amt,
journal_entry_id = entry.id)একটা contract-এর সব obligation-এর জন্য এক entry না আলাদা — আলাদা, source_id = obligation — যাতে ভুল হলে একটাই উল্টাতে হয়।
Idempotent: একই period-এ দুবার চালালে দ্বিতীয়বার amt = 0, কিছু হয় না — কারণ recognized_to_date বেড়ে গেছে। এটা অধ্যায় ৪৬-এর নিয়মের প্রথম প্রয়োগ: job-এর ফল state-এ থাকে, job-এর স্মৃতিতে নয়।
ঋণাত্মক amt — milestone acceptance বাতিল, বা percent_complete-এর অনুমান বেড়ে গেল (cost বেশি লাগবে, pct কমল)। নিয়ম: গত মাসের entry বদলানো নয়; এ মাসে সমন্বয় — Dr revenue, Cr 2180/1170, “change in estimate”। আগের মাস ছাপা হয়ে গেছে; সত্যটা তখন যা জানা ছিল তাই ছিল।
Schedule তৈরি
buildSchedule(ob):
straight_line: প্রতিটি মাস service_from..service_to:
planned = earnedToDate(ob, month_end) − আগের মাস পর্যন্ত
milestone: প্রতিটি milestone: planned[month(planned_date)] += m.amount
point_in_time: planned[month(expected_delivery)] = amount
percent_complete: planned estimate থেকে (project plan) — বদলাবে
assert Σ planned == ob.amountplanned হলো forecast — “সামনের ছয় মাসে কত revenue আসার কথা” (backlog report)। recognized হলো সত্য। দুটোর পার্থক্য: দেরি বা এগিয়ে থাকা project।
Report
১. Revenue বনাম Billing বনাম Cash — মাস-ধরে (section ৪-এর table)
revenue = GL 4xxx, posting_date
billing = sales_invoice_lines.net, invoice_date
cash = payments (receipt, customer), doc_date
২. Deferred rollforward — মাস-ধরে
opening + billed(deferred অংশ) − recognized == closing == GL 2180
contract-ধরে ভাঙা
৩. Unbilled aging — position < 0 contract-গুলো, কতদিন ধরে
60 দিনের বেশি → "bill করুন!" — এটা নগদের leak
৪. Backlog (remaining performance obligation)
Σ (ob.amount − ob.recognized_to_date) WHERE active
সামনের মাসে planned schedule থেকে forecastতৃতীয়টা developer-দের কাছে অচেনা, finance-এর কাছে সবচেয়ে দামি: unbilled মানে কাজ হয়ে গেছে, invoice কেউ লেখেনি — গ্রাহক খুশি, cash নেই।
রাতের যাচাই
৩৭. GL 2180 == Σ max(position(c), 0) সব contract
+ অধ্যায় ১৩-এর service_from-ভিত্তিক deferred
৩৮. GL 1170 == Σ max(−position(c), 0)
৩৯. প্রতিটি contract: billed_to_date == Σ invoice line net (issued, obligation_id → c)
recognized_to_date == Σ recognition_schedule.recognized_amount
৪০. প্রতিটি obligation: recognized_to_date ≤ amount; Σ schedule.planned == amount
৪১. recognition_schedule.recognized_amount > 0 কিন্তু journal_entry_id IS NULL → ০ সারি
৪২. milestone accepted_at IS NOT NULL, acceptance_doc_url IS NULL → ০ সারি
(CHECK আছে, তবু — অধ্যায় ৭)
৪৩. unbilled 60 দিনের বেশি → সতর্কতা
contract active কিন্তু ৯০ দিন কোনো recognition নেই → সতর্কতাTest হিসেবে
test "contract-এর invoice revenue স্পর্শ করে না":
c = contract(নদী, ob1 milestone 6,00,000, ob2 straight 1,20,000)
issue(invoice, line(ob1, 2,40,000))
assert Δ 4110 == 0
assert Δ 2180 == +2,40,000
assert position(c) == +2,40,000
test "recognition: deferred আগে, তারপর unbilled":
accept(ob1.design 20-11); recognize(2025-11)
assert Δ 2180 == −1,20,000; Δ 1170 == 0; Δ 4110 == +1,20,000
accept(ob1.build 22-12); recognize(2025-12)
assert Δ 2180 == −1,20,000; Δ 1170 == +1,80,000; Δ 4110 == +3,00,000
assert position(c) == −1,80,000
test "invoice: unbilled আগে খালি, বাকি deferred":
... position −3,60,000
issue(invoice, line(ob1, 3,60,000), line(ob2, 1,20,000))
assert Δ 1170 == −3,60,000
assert Δ 2180 == +1,20,000
assert Δ 4110 == 0
test "idempotent":
recognize(2025-11); g = snapshot(gl)
recognize(2025-11)
assert snapshot(gl) == g
test "acceptance ছাড়া milestone আয় নয়":
milestone.accepted_at = NULL
recognize(2025-11)
assert Δ 4110 == 0
assert rejects accept(milestone, doc_url = NULL)
test "straight-line: Σ == amount, rounding শেষ মাসে":
ob = straight_line(1,00,000, 12 মাস)
assert Σ schedule.planned == 1,00,000
assert schedule[12].planned == 1,00,000 − 11 × 8,333
test "একটা contract-এ deferred আর unbilled একসাথে নয়":
... যেকোনো কাজের পরে, প্রতিটি contract:
assert not (deferredBalance(c) > 0 and unbilledBalance(c) > 0)
test "cut-off: বছরের শেষ দিনের invoice আয় বাড়ায় না":
issue(invoice, date = 31-12, line(ob, 5,00,000)); ob-এর কিছু accepted নেই
assert revenue(2025) অপরিবর্তিত৬. Financial Statement Impact
INCOME STATEMENT নভ ডিসে জান ফেব
4110 Software Dev. 1,20,000 3,00,000 1,80,000 0
4120 Support 0 0 0 10,000
(invoice-এর অঙ্ক কোথাও নেই; cash-এর অঙ্ক কোথাও নেই)
BALANCE SHEET (মাস শেষে, নদী গ্রুপ-সংক্রান্ত)
1130 A/R 0 0 5,52,000 0
1170 Unbilled Revenue 0 1,80,000 0 0
2180 Deferred Revenue 1,20,000 0 0 1,10,000
2140 VAT Payable (এই) 36,000 — 72,000 —
CASH FLOW (operating)
গ্রাহক থেকে 2,76,000 0 0 5,52,000তিনটি statement তিনটি সংখ্যা দেখায় — আর তিনটিই ঠিক। Income Statement কাজ মাপে, Balance Sheet ফাঁক মাপে (কে কাকে কত দেবে), Cash Flow টাকা মাপে। Recognition হলো তিনটির মধ্যে সেতু: revenue − ΔA/R − Δunbilled + Δdeferred = cash (VAT বাদে) — অধ্যায় ২২-এর indirect method-এর প্রথম সূত্র।
Balance Sheet-এ একটা সূক্ষ্মতা: 1170 Unbilled আর 1130 A/R দুটোই “গ্রাহকের কাছে পাওনা”, কিন্তু আলাদা দেখানো হয় — A/R-এর due date আছে, তাগাদা দেওয়া যায়; unbilled-এর কিছুই নেই, আগে invoice লিখতে হবে। Auditor দুটোকে আলাদা চোখে দেখেন: বেশি unbilled মানে হয় billing দেরি, নয় আশাবাদী recognition।
৭. Common Developer Mistakes
| ভুল | কী ঘটে | সঠিক পথ |
|---|---|---|
| Invoice-এ revenue, contract থাকলেও | ৪০% advance invoice-এ ৪০% আয়, কাজ ২০% | contract line → 1170/2180, revenue শুধু job থেকে |
| Receipt-এ revenue | cash basis, না জেনে | receipt শুধু A/R কমায় |
| Milestone “done” পতাকা developer-এর হাতে | আশাবাদ আয় হয়ে যায় | accepted_at + doc, গ্রাহকের নাম |
| Deferred আর unbilled দুটোই এক contract-এ | Balance Sheet ফোলে (asset ও liability দুটোই) | position নিট, একটাই |
| Recognition-এ position না দেখে সবসময় Dr 2180 | 2180 ঋণাত্মক (debit balance liability) | deferred যতটা আছে, বাকি 1170 |
| Straight-line-এ round, Σ ≠ amount | ৪ টাকা চিরকাল deferred-এ | শেষ মাস বাকিটা, রাতের যাচাই ৪০ |
| Bundle-এ ভাগ নেই, দাম যেমন লেখা | ছাড় এক obligation-এ চাপে, আয় এগিয়ে আসে | standalone price দিয়ে আনুপাতিক |
| Recognition entry-র posting_date = job চালানোর দিন | ০২-১২-এ চালালে নভেম্বরের আয় ডিসেম্বরে | period_end |
| অনুমান বদলে গত মাসের entry বদলানো | ছাপা report বদলায় | এ মাসে সমন্বয়, change in estimate |
| Job-এর “চালানো হয়েছে” পতাকা, state নয় | পতাকা হারালে দুবার আয় | recognized_to_date থেকে amt, idempotent |
| Unbilled report নেই | কাজ হয়ে গেছে, কেউ invoice লেখেনি | unbilled aging, ৬০ দিনে সতর্কতা |
| বেতন revenue-র মাসে “match” করা | period cost পিছিয়ে, লাভ মসৃণ কিন্তু মিথ্যা | বেতন যে মাসে কাজ |
| ১২ মাসের license মাস ১-এ পুরো খরচ | ১ মাসে লোকসান, ১১ মাস “বিনামূল্যে” | 1150, মাসে মাসে (অধ্যায় ১৮) |
| Cash report আর accrual report এক table-এ | manager দুটো “লাভ” দেখে বিভ্রান্ত | আলাদা report, আলাদা নাম |
প্রথম আর চতুর্থ সবচেয়ে সাধারণ। প্রথমটা প্রায় প্রতিটি “invoice-ভিত্তিক” system-এর জন্মদোষ — invoice লেখা মানেই আয়। সেটা বদলাতে হলে আয়ের দরজা একটাই রাখতে হয়: recognition। চতুর্থটা তখন হয় যখন deferred আর unbilled দুটো আলাদা module-এ, আলাদা developer-এর হাতে — কেউ position-এর কথা ভাবেনি।
৮. Exercises
সেট ক — কখন আয়
১। ১৫-১১-এ ৩ দিনের training-এর invoice 90,000, training ০৫–০৭-১২,
টাকা ২০-১১। নভেম্বরের আয়? ডিসেম্বরের? তিনটি entry।
২। Perpetual license 2,00,000 + প্রথম বছরের support 50,000, একটা
invoice ০১-১২, key দেওয়া হলো ০১-১২। ডিসেম্বরের আয়? obligation
কয়টা? পরের নভেম্বরে?
৩। ২ নম্বরে package দাম 2,25,000 (standalone 2,00,000 + 50,000)।
ভাগ করুন। ডিসেম্বরের আয়?
৪। SaaS: বার্ষিক 1,20,000, ০১-১১ থেকে, invoice ও টাকা ০১-১১।
schedule লিখুন। ৩১-০৩-এ 2180 কত?
৫। Fixed-price project 10,00,000, percent_complete, অনুমিত খরচ
6,00,000। মাস ১-এ খরচ 1,50,000, মাস ২-এ 2,10,000 (মোট 3,60,000)।
দুই মাসের আয়। মাস ২-এর শেষে অনুমান বদলে 8,00,000 হলো — মাস ২-এর
আয় আবার হিসাব করুন। মাস ১-এর entry বদলাবে?সেট খ — position ও cut-off
৬। Contract 5,00,000: billed 3,00,000, recognized 4,00,000। position?
কোন account, কত? এখন 1,50,000-এর invoice হলে entry?
৭। Contract-এর দুটো obligation: A (deferred 50,000 বাকি), B (এ মাসে
earned 80,000)। recognition entry — 2180 থেকে কত, 1170 কত?
(ইঙ্গিত: position contract-ধরে, obligation-ধরে নয়)
৮। ৩০-১১-এ: (ক) invoice 30-11, delivery 02-12 (খ) delivery 28-11,
invoice 04-12 (গ) advance 25-11, কাজ ডিসেম্বর (ঘ) কাজ নভেম্বর,
bill এল 06-12। প্রতিটি: নভেম্বরের Income Statement-এ আছে? কোন
account-এ কী থাকে ৩০-১১-এ?
৯। Deferred rollforward: opening 2,20,000, এ মাসে deferred-অংশ
billed 1,20,000, recognized 1,40,000। closing? GL 2180 দেখাচ্ছে
1,95,000 — পার্থক্য 5,000। সম্ভাব্য কারণ তিনটি।সেট গ — কোনটা ভুল
১০। contract invoice: Dr A/R 2,76,000 / Cr 4110 2,40,000 / Cr 2140 36,000
১১। recognition, position −1,80,000: Dr 2180 1,80,000 / Cr 4110 1,80,000
১২। milestone.accepted = true, set করলেন project manager (আপনার)
১৩। recognizeRevenue() posting_date = today()
১৪। straight_line 1,00,000/12 → প্রতি মাস 8,333.33, DECIMAL(18,2)
১৫। "revenue by month" report: SELECT month(doc_date), SUM(amount) FROM payments
১৬। developer বেতন ডিসেম্বরে 1150 Prepaid-এ, "revenue জানুয়ারিতে"সেট ঘ — নকশা
১৭। অধ্যায় ১৩-এর service_from/to-ভিত্তিক deferred আর এই অধ্যায়ের
obligation-ভিত্তিক — দুটো নিয়ম একসাথে থাকবে, নাকি প্রথমটাকে
"implicit contract" বানিয়ে দ্বিতীয়তে মেলাবেন? migration কী?
১৮। Contract cancelled হলো মাঝপথে: recognized 4,20,000, billed
2,40,000, গ্রাহক আর দেবেন না, কাজ থামল। entry? unbilled 1,80,000-এর
কী হবে — write-off (অধ্যায় ১৪), নাকি revenue reverse? কখন কোনটা?
১৯। percent_complete-এ "cost to date" আসে project ledger থেকে
(অধ্যায় ৩৫)। বেতন cost-এ ঢুকবে কীভাবে — timesheet? developer
৫ ঘণ্টা কম লিখলে আয় কমে যায়। এই সম্পর্কটা কি ঠিক? বিকল্প?
২০। Recognition job মাসের শেষে চলে। কিন্তু dashboard "এ মাসের আয় এখন
পর্যন্ত" চায় ১৫ তারিখে। ledger-এ entry ছাড়া কীভাবে দেখাবেন?
earnedToDate(ob, today) দিয়ে virtual? তার ঝুঁকি?উত্তর আছে Workbook-এর Answer Key, অধ্যায় ১৭-এ।
৯. Developer Challenge
একটি RevenueRecognitionService নকশা করুন — contract থেকে schedule, মাসিক job, position, invoice-এর সাথে সংযোগ, আর তিনটি report।
যা যা নকশা করবেন:
১. চার table —
contracts,contract_obligations,contract_milestones,recognition_schedule— আরsales_invoice_lines.obligation_id। অধ্যায় ১৩-এরservice_from/toকোথায় যাবে: থাকবে, নাকি প্রতিটি invoice line-ই একটা implicit obligation? দুটোর trade-off।২.
earnedToDate()চার পদ্ধতিতে — straight_line-এ দিন/মাস config ও rounding, milestone-এ acceptance, percent_complete-এ অনুমান বদল (এ মাসে সমন্বয়, আগের মাস নয়)। প্রতিটির জন্য test: Σ == amount, ঋণাত্মক সমন্বয়, idempotent।৩. Contract position —
billed_to_date,recognized_to_dateসংরক্ষিত, lock, একই transaction-এ update;creditSideForLine()আরrecognizeRevenue()দুটোই position থেকে; “একটা contract-এ দুটো একসাথে নয়” — কোন স্তরে নিশ্চিত করবেন (function, CHECK, রাতের যাচাই)?৪. Bundle allocation —
standalone_priceথেকেamount, override-এ কারণ ও অনুমোদন; contract-এর মাঝে obligation যোগ হলে (change order — অধ্যায় ৩৫) নতুন করে ভাগ, নাকি শুধু নতুনটা?৫. মাসিক job — period খোলা যাচাই, obligation-ধরে entry,
source_type = 'recognition', ব্যর্থ হলে মাঝপথে (৫০টা contract-এর ২০টা হলো) — কী অবস্থা, আবার চালালে ঠিক হয় কি (idempotent)? Period বন্ধের সাথে সম্পর্ক: recognition না চালিয়ে period বন্ধ করা যাবে? (অধ্যায় ২৬, ৪২)৬. তিনটি report — revenue/billing/cash by month, deferred rollforward (contract-ধরে, GL-এর সাথে মেলানো), unbilled aging + backlog। Dashboard-এর জন্য “মাসের মাঝে এখন পর্যন্ত আয়” — virtual, ledger নয়; কীভাবে আলাদা করে দেখাবেন যাতে কেউ সেটাকে report ভেবে না ছাপে?
৭. Cut-off নিয়ন্ত্রণ — contract-এর invoice কখনো revenue-তে নয়; obligation ছাড়া invoice line-এ
service_fromবাধ্যতামূলক কি না (নীতি); বছরের শেষ সপ্তাহে issue হওয়া invoice-এর একটা report auditor-এর জন্য।৮. রাতের যাচাই ৩৭–৪৩। ৩৭ ব্যর্থ হলে: GL ভুল, না position ভুল — কীভাবে বের করবেন? (ইঙ্গিত: contract-ধরে ভাঙুন, অধ্যায় ১৪-এর তিন স্তরের পদ্ধতি।)
৩ আর ৫ নম্বরটাই আসল পরীক্ষা। Contract position হলো এই বইয়ের প্রথম জায়গা যেখানে একটা সংখ্যার চিহ্ন ঠিক করে সেটা asset না liability — অধ্যায় ১১-এর overdraft, অধ্যায় ১৪-এর credit balance — সেই ধারণারই পূর্ণ রূপ। আর মাসিক job হলো প্রথম period-ভিত্তিক batch যা আয় তৈরি করে — এটা ঠিকমতো idempotent না হলে অধ্যায় ১৮-এর accrual, ২৫-এর adjusting, ৩০-এর depreciation — প্রতিটি job একই bug আবার আনবে।
১০. Summary Card
নিয়ম
আয় = কাজ সম্পন্ন (point) / যতটা হলো (over time) invoice নয়, টাকা নয়
খরচ = সুবিধা ভোগ bill নয়, payment নয়পাঁচ ধাপ
contract → obligation (কয়টা আলাদা?) → দাম → ভাগ (standalone) → recognize (কখন/কতটা)Over time
straight_line amount × elapsed/total শেষ মাস rounding নেয়
milestone Σ accepted (গ্রাহকের sign-off + doc)
percent_complete amount × cost/estimate অনুমান বদল → এ মাসে সমন্বয়Position
position = billed − recognized > 0 → 2180 (liab.) < 0 → 1170 (asset)
contract-ধরে নিট; একসাথে দুটো নয়
invoice: Cr 1170 (যতটা আছে), বাকি Cr 2180 — revenue নয়
recognition: Dr 2180 (যতটা আছে), বাকি Dr 1170 — Cr revenue2×2
document/টাকা আগে কাজ/ভোগ আগে
আয় 2180 Deferred (L) 1170 Unbilled (A)
খরচ 1150 Prepaid (A) 2135 Accrued (L) ← অধ্যায় ১৮Cut-off
ঘটনার তারিখ, document-এর নয়; recognition posting_date = মাসের শেষ দিনDeveloper checklist
□ contracts / obligations / milestones / recognition_schedule
□ sales_invoice_lines.obligation_id → creditSideForLine() position থেকে
□ contract invoice কখনো revenue-তে নয় — আয়ের একটাই দরজা
□ milestone: accepted_at + accepted_by + doc (CHECK)
□ earnedToDate() চার পদ্ধতি; Σ schedule == amount
□ recognizeRevenue(): period_end, obligation-ধরে entry, idempotent (state থেকে)
□ position সংরক্ষিত + lock + রাতে পুনর্গণনা
□ bundle: standalone_price দিয়ে ভাগ, override-এ কারণ
□ অনুমান বদল: এ মাসে সমন্বয়, আগের entry নয়
□ report: revenue/billing/cash, deferred rollforward, unbilled aging, backlog
□ cash view payments থেকে আলাদা report — accrual-এর সাথে মেশাবেন না
□ রাতে: 2180/1170 == Σ position, billed/recognized == গণিত, ≤ amount,
JE link, acceptance doc, unbilled বয়স, নিষ্ক্রিয় contractপরবর্তী অধ্যায়
অধ্যায় ১৮ — Accrual ও Prepaid: 2×2-এর উপরের সারি এই অধ্যায়ে; নিচের সারি পরের অধ্যায়ে। নদী গ্রুপের project-এ ৬০,০০০-এর বার্ষিক license ১২ মাসে ভাগ হবে (prepaid), ডিসেম্বরের subcontractor-এর bill জানুয়ারিতে এলেও খরচ ডিসেম্বরে বসবে (accrued), আর অক্টোবরের বিদ্যুৎ bill — যেটা অধ্যায় ১৫-এ “আসেনি” — আনুমানিক অঙ্কে খাতায় উঠবে, bill এলে সমন্বয় হবে। তিনটিই মাসের শেষের কাজ, তিনটিই উল্টে যাওয়ার নিয়ম মানে (reversing entry) — আর তিনটিই এই অধ্যায়ের recognition job-এর ভাই: period-ভিত্তিক, idempotent, বাকিটা state-এ।