Skip to Content
Go Realm v1 is released 🎉
Accountingবই (Print Edition)অধ্যায় ১৭: Revenue ও Expense Recognition

অধ্যায় ১৭ — 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 হলো payments table-এর GROUP BY month। Accrual হলো posting_date দিয়ে GL। আপনি যদি “মাসের আয়” বের করতে payments join করেন — 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_cost

Bundle

obligation.amount = contract.total × standalone_price / Σ standalone_price

Contract position

position = billed_to_date − recognized_to_date > 0 → 2180 Deferred Revenue (Cr) < 0 → 1170 Unbilled Revenue (Dr) contract-ধরে নিট; দুটো একসাথে নয়

Entry

ঘটনাDebitCredit
Invoice, contract-এর (unbilled আছে)A/RUnbilled Revenue (যতটা আছে), Deferred Revenue (বাকি), VAT
Recognize (deferred আছে)Deferred Revenue (যতটা আছে), Unbilled Revenue (বাকি)Revenue
Prepaid → খরচ (অধ্যায় ১৮)ExpensePrepaid Expense
খরচ, bill আসেনি (অধ্যায় ১৮)ExpenseAccrued 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 cost

Project-এর লাভ, 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.amount

planned হলো 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-এ revenuecash basis, না জেনেreceipt শুধু A/R কমায়
Milestone “done” পতাকা developer-এর হাতেআশাবাদ আয় হয়ে যায়accepted_at + doc, গ্রাহকের নাম
Deferred আর unbilled দুটোই এক contract-এBalance Sheet ফোলে (asset ও liability দুটোই)position নিট, একটাই
Recognition-এ position না দেখে সবসময় Dr 21802180 ঋণাত্মক (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 revenue

2×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-এ।