বিলিং লেজার স্কিমা যা মিল মেলে: ইনভয়েস ও পেমেন্ট
কিভাবে আলাদা ইনভয়েস, পেমেন্ট, ক্রেডিট ও অ্যাডজাস্টমেন্ট রেখে এমন একটি বিলিং লেজার স্কিমা ডিজাইন করবেন যাতে ফাইন্যান্স সহজে টোটাল রিকনসাইল ও অডিট করতে পারে।

কেন বিলিং ডেটা মিলানো বন্ধ করে দেয়
ফাইন্যান্সের জন্য “রিকনসিল” মানে সহজ: রিপোর্টে থাকা মোটগুলো সোর্স রেকর্ডগুলোর সাথে মিলে এবং প্রতিটি সংখ্যাকে ট্রেস করা যায়। যদি ওই মাসে বলা হয় $12,430 সংগৃহীত হয়েছে, আপনাকে ঠিক কোন পেমেন্ট (আর কোনো রিফান্ড) সেই সংখ্যাটি করেছে তা দেখাতে হবে, কোন কোন ইনভয়েসে তা প্রয়োগ হয়েছে এবং প্রতিটি পার্থক্য একটি তারিখযুক্ত রেকর্ড দিয়ে ব্যাখ্যা করতে হবে।
বিলিং ডেটা সাধারণত তখন মিলানো বন্ধ করে যখন ডাটাবেস ফলাফলগুলো স্টোর করে ততোক্ষণ না যে ঘটেছিল তা। paid_amount, balance, বা amount_due মতো কলামগুলো অ্যাপ্লিকেশন লজিক দ্বারা সময়ের সঙ্গে আপডেট হয়। একটি বাগ, একটি রিট্রাই, বা একটি ম্যানুয়াল “ফিক্স” ইতিহাস চুপচাপ বদলে দিতে পারে। সপ্তাহ পর ইনভয়েস টেবিল বলে করবে ইনভয়েস "paid", কিন্তু পেমেন্ট রো গুলো যোগ করলে মিলবে না, অথবা কোনো রিফান্ড আছে কিন্তু মিলতে একটি ক্রেডিট নেই।
আরেকটি সাধারণ কারণ হলো বিভিন্ন ডকুমেন্ট টাইপ একসাথে মিশানো। একটি ইনভয়েস পেমেন্ট নয়। একটি ক্রেডিট মেমো রিফান্ড নয়। একটি অ্যাডজাস্টমেন্ট ডিসকাউন্টের সমান নয়। যখন এগুলো এক “transactions” রো তে অপশনাল ফিল্ডগুলোর সঙ্গে চাপা হয়, রিপোর্টিং অনুমান ভিত্তিক হয়ে যায় এবং অডিটে তর্ক দেখা দেয়।
মূল অসঙ্গতি সহজ: অ্যাপগুলি প্রায়ই বর্তমান অবস্থা নিয়ে চিন্তা করে ("access active কি?"), যখন ফাইন্যান্স চায় ট্রেইল ("কি ঘটল, কখন এবং কেন?")। একটি বিলিং লেজার স্কিমা উভয়কেই সমর্থন করবে, কিন্তু ট্রেসেবিলিটি প্রধান হতে হবে।
এটি অর্জন করার জন্য ডিজাইন করুন:
- কাস্টমার, ইনভয়েস, এবং অ্যাকাউন্টিং পিরিয়ড অনুযায়ী স্পষ্ট টোটাল
- প্রতিটি পরিবর্তন নতুন রো হিসেবে রেকর্ড (ওভাররাইট নয়)
- ইনভয়েস থেকে পেমেন্ট, ক্রেডিট, রিফান্ড, এবং অ্যাডজাস্টমেন্ট পর্যন্ত সম্পূর্ণ চেইন
- র কাঁচা এন্ট্রিগুলো থেকে টোটালগুলো পুনঃগণনা করলে একই উত্তর পাওয়ার সক্ষমতা
উদাহরণ: যদি একটি কাস্টমার $100 পরিশোধ করে, তারপর $20 ক্রেডিট পায়, আপনার রিপোর্টগুলো হওয়া উচিত $100 সংগ্রহ, $20 ক্রেডিট, এবং $80 নেট, মূল ইনভয়েস অ্যামাউন্ট পরিবর্তন না করেই।
ইনভয়েস, পেমেন্ট, ক্রেডিট, এবং অ্যাডজাস্টমেন্ট আলাদা রাখুন
যদি আপনি একটি সামঞ্জস্যপূর্ণ বিলিং লেজার স্কিমা চান, প্রতিটি ডকুমেন্ট টাইপকে ভিন্ন ধরনের ইভেন্ট হিসেবে বিবেচনা করুন। এগুলোকে একটি “transactions” টেবিলে মিশানো দেখতে অ্যাকোপ কিন্তু তা মানে স্পষ্টতা নষ্ট করা।
একটি ইনভয়েস একটি দাবী: “কাস্টমার আমাদের কাছে টাকা দেয়।” এটিকে একটি হেডার (কাস্টমার, ইনভয়েস নম্বর, ইস্যু তারিখ, ডিউ তারিখ, মুদ্রা, টোটাল) এবং আলাদা লাইনের আইটেম (কি বিক্রি হয়েছে, পরিমাণ, ইউনিট মূল্য, ট্যাক্স বিভাগ) হিসেবে স্টোর করুন। গতি জন্য হেডার টোটাল রাখা ঠিক আছে, তবে আপনাকে সবসময় লাইনগুলো থেকে তা ব্যাখ্যা করতে সক্ষম হতে হবে।
একটি পেমেন্ট টাকা-চলাচল: “কাস্টমার থেকে আমাদের কাছে টাকা গেছে।” কার্ড ফ্লোতে প্রায়ই আপনি authorization (ব্যাংক অনুমোদন) এবং capture (টাকা আসলেই নেওয়া হয়েছে) দেখতে পাবেন। অনেক সিস্টেম authorization গুলো অপারেশনাল রেকর্ড হিসেবে রাখে এবং কেবল captured পেমেন্টগুলো লেজারে রাখে, যাতে ক্যাশ রিপোর্টিং বাড়িয়ে না যায়।
একটি ক্রেডিট মেমো কত কাস্টমার দেন তা কমায় কিন্তু প্রয়োজনীয়ভাবে টাকা ফেরত পাঠায় না। একটি রিফান্ড হল টাকা বেরোনো। এগুলো প্রায়ই একসাথে ঘটে, কিন্তু একই জিনিস নয়।
- ইনভয়েস: রিসিভেবল এবং রেভেন্যু (বা ডিফার্ড রেভেন্যু) বাড়ায়
- পেমেন্ট: ক্যাশ বাড়ায় এবং রিসিভেবল কমায়
- ক্রেডিট মেমো: রিসিভেবল কমায়
- রিফান্ড: ক্যাশ কমায়
একটি অ্যাডজাস্টমেন্ট হলো টীম দ্বারা করা একটি সংশোধন যখন বাস্তবতা রেকর্ডের সাথে মিলছে না। অ্যাডজাস্টমেন্টগুলোর কনটেক্সট দরকার যাতে ফাইন্যান্স এগুলো বিশ্বাস করে। স্টোর করুন যে কে তৈরি করেছে, কখন পোস্ট হয়েছে, একটি রিজন কোড এবং একটি সংক্ষিপ্ত নোট। উদাহরণ: “rounding miatt 0.03 write off”, বা “legacy balance migrate।”
একটি ব্যবহারিক নিয়ম: জিজ্ঞেস করুন, “এটি কি থাকবে যদি কেউ একটিও ভুল না করত?” ইনভয়েস, পেমেন্ট, ক্রেডিট মেমো, এবং রিফান্ড থাকবে। অ্যাডজাস্টমেন্টগুলো বিরল হওয়া উচিত, স্পষ্টভাবে লেবেল করা এবং সহজে পর্যালোচনাযোগ্য।
এমন একটি লেজার মডেল বেছে নিন যা ফাইন্যান্স অডিট করতে পারে
একটি রিকনসাইলিং বিলিং লেজার স্কিমার মূল ধারণা: ডকুমেন্টগুলো কী ঘটেছে তা বর্ণনা করে, এবং লেজার পোস্টিংগুলো টোটাল প্রমাণ করে। একটি ইনভয়েস, পেমেন্ট, বা ক্রেডিট মেমো ডকুমেন্ট। লেজার হল সেই এন্ট্রিগুলোর সেট যা যোগ করলে সঠিক টোটাল আসে।
ডকুমেন্ট বনাম পোস্টিং (উভয়ই স্টোর করুন)
ডকুমেন্টগুলো রাখুন (ইনভয়েস হেডার ও লাইন, পেমেন্ট রিসিট, ক্রেডিট মেমো) কারণ মানুষ এগুলো পড়বে। কিন্তু রিকনসিলিয়েশনের সোর্স অব ট্রুথ হিসেবে কেবল ডকুমেন্ট টোটালগুলোর ওপর নির্ভর করবেন না।
বরং, প্রতিটি ডকুমেন্টকে লেজার টেবিলে এক বা একাধিক অপরিবর্তনীয় এন্ট্রি হিসেবে পোস্ট করুন। তখন ফাইন্যান্স এন্ট্রিগুলোকে অ্যাকাউন্ট, কাস্টমার, মুদ্রা, এবং পোস্টিং তারিখ অনুযায়ী যোগ করে প্রতিবার একই উত্তর পাবে।
একটি সহজ অডিট-ফ্রেন্ডলি মডেল কয়েকটি নিয়ম অনুসরণ করে:
- অপরিবর্তনীয় এন্ট্রি: পোস্ট করা পরিমাণ কখনো এডিট করবেন না; পরিবর্তন হলে নতুন এন্ট্রি তৈরি করুন।
- স্পষ্ট পোস্টিং ইভেন্ট: প্রতিটি ডকুমেন্ট একটি ইউনিক রেফারেন্স সহ একটি পোস্টিং ব্যাচ তৈরি করে।
- ব্যালান্সড লজিক: এন্ট্রিগুলো কোম্পানি স্তরে সঠিকভাবে নেট হবে (প্রায়শই ডেবিট সমান ক্রেডিট)।
- আলাদা তারিখ: ডকুমেন্ট তারিখ (যা কাস্টমার দেখে) এবং পোস্টিং তারিখ (যা রিপোর্টিংয়ে পড়ে) আলাদা রাখুন।
- স্থিতিশীল রেফারেন্স: বাহ্যিক রেফারেন্স (ইনভয়েস নম্বর, পেমেন্ট প্রসেসর ID) অভ্যন্তরীণ ID-এর পাশাপাশি স্টোর করুন।
ন্যাচারাল কি বনাম সারগেট আইডি
জয়েন এবং পারফরম্যান্সের জন্য সারগেট আইডি ব্যবহার করুন, কিন্তু একটি স্থিতিশীল ন্যাচারাল কি রাখুন যা মাইগ্রেশন ও রি-ইম্পোর্টে টিকে থাকে। ফাইন্যান্স অনেক দিন পরে “Invoice INV-10483” চাইবে, তাই ইনভয়েস নম্বর এবং প্রোভাইডার ID-কে ফার্স্ট-ক্লাস ক্ষেত্র হিসেবে বিবেচনা করুন।
ইতিহাস মুছা নয়, রিভার্সাল করুন
যখন কিছু উল্টে ফেলতে হবে, মুছুন বা ওভাররাইট করবেন না। একটি রিভার্সাল পোস্ট করুন: মূল পরিমাণের বিপরীত চিহ্নের নতুন এন্ট্রি, যাকে মূল পোস্টিংয়ের সাথে লিংক করা থাকবে।
উদাহরণ: ভুল ইনভয়েসে $100 পেমেন্ট হলে দুই ধাপে সমাধান হবে: মিসঅ্যাপ্লাইড পোস্টিং রিভার্স করুন, তারপর সঠিক ইনভয়েসে নতুন অ্যাপ্লিকেশন পোস্ট করুন।
ধাপে ধাপে স্কেমা ব্লুপ্রিন্ট (টেবিল ও কী)
একটি বিলিং লেজার স্কিমা সহজে রিকনসাইল করতে প্রতিটি ডকুমেন্ট টাইপের জন্য আলাদা টেবিল রাখা এবং তাদের স্পষ্ট অ্যালোকেশন রেকর্ড (পরে অনুমান করা নয়) দিয়ে কনেক্ট করা থাকা ভালো।
কোর টেবিলগুলোর ছোট সেট দিয়ে শুরু করুন, প্রতিটি স্পষ্ট প্রাইমারি কী (UUID বা bigserial) এবং প্রয়োজনীয় ফরেন কী সহ:
- customers:
customer_id(PK), পাশাপাশি স্থিতিশীল আইডেন্টিফায়ার যেমনexternal_ref(unique) - invoices:
invoice_id(PK),customer_id(FK),invoice_number(unique),issue_date,due_date,currency - invoice_lines:
invoice_line_id(PK),invoice_id(FK),line_type,description,qty,unit_price,tax_code,amount - payments:
payment_id(PK),customer_id(FK),payment_date,method,currency,gross_amount - credits:
credit_id(PK),customer_id(FK),credit_number(unique),credit_date,currency,amount
তারপর যোগ করুন টেবিলগুলো যা টোটালগুলোকে অডিটেবল করে: allocations। একটি পেমেন্ট বা ক্রেডিট একাধিক ইনভয়েস কভার করতে পারে, এবং একটি ইনভয়েস একাধিক পেমেন্ট দ্বারা পেড হতে পারে।
জয়েন টেবিলগুলো ব্যবহার করুন তাদের নিজস্ব কী সহ (শুধু কম্পোজিট কী নয়):
- payment_allocations:
payment_allocation_id(PK),payment_id(FK),invoice_id(FK),allocated_amount,posted_at - credit_allocations:
credit_allocation_id(PK),credit_id(FK),invoice_id(FK),allocated_amount,posted_at
সবশেষে, অ্যাডজাস্টমেন্ট আলাদা রাখুন যাতে ফাইন্যান্স কি বদলিয়েছে এবং কেন সহজে দেখতে পারে। একটি adjustments টেবিল টার্গেট রেকর্ডকে invoice_id (nullable) দিয়ে রেফারেন্স করতে পারে এবং ডেল্টা এমাউন্ট স্টোর করবে, ইতিহাস পুনঃলিখন না করে।
মনিটরি পোস্টিং যেখানে-ই করা হবে সব জায়গায় অডিট ফিল্ড যোগ করুন:
created_at,created_byreason_code(write-off, rounding, goodwill, chargeback)source_system(manual, import, Stripe, support tool)
ক্রেডিট, রিফান্ড এবং রাইট-অফ টুট-ভেঙে টোটাল টিকে রাখা
অধিকাংশ রিকনসিলিয়েশন সমস্যা শুরু হয় যখন ক্রেডিট ও রিফান্ডকে “নেগেটিভ পেমেন্ট” হিসেবে রেকর্ড করা হয়, অথবা রাইট-অফ গুলো ইনভয়েস লাইনে মিশে যায়। একটি পরিষ্কার বিলিং লেজার স্কিমা প্রতিটি ডকুমেন্ট টাইপকে আলাদা রেকর্ড হিসেবে রাখে, এবং একমাত্র জায়গা যেখানে তারা ইন্টারঅ্যাক্ট করে তা হল স্পষ্ট অ্যালোকেশন।
একটি ক্রেডিট দেখানো উচিত কেন আপনি কাস্টমারের দায় কমালেন। যদি এটি একটি ইনভয়েসে প্রয়োগ হয়, একটি ক্রেডিট মেমো বানিয়ে সেটি ঐ ইনভয়েসে অ্যালোকেট করুন। যদি এটি একাধিক ইনভয়েসে প্রযোজ্য হয়, একই ক্রেডিট মেমোকে একাধিক ইনভয়েসে অ্যালোকেট করুন। ক্রেডিট এক ডকুমেন্ট হিসেবে থাকবে ও অনেক অ্যালোকেশন থাকতে পারবে।
রিফান্ড পেমেন্ট-সদৃশ ইভেন্ট; এগুলো নেগেটিভ পেমেন্ট নয়। রিফান্ড হল টাকা আপনার কাছ থেকে বেরোনো, তাই এটিকে একটি পৃথক রেকর্ড হিসেবে ট্রিট করুন (প্রায়ই রেফারেন্স হিসেবে মূল পেমেন্টের সাথে যুক্ত), তারপর একটি পেমেন্টের মতোই এটি অ্যালোকেট করুন। এতে ব্যাংক স্টেটমেন্টে ইনকামিং পেমেন্ট ও আউটগোয়িং রিফান্ড দুটোকেই স্পষ্ট ট্রেইলে রাখা যায়।
পার্শিয়াল পেমেন্ট এবং পার্শিয়াল ক্রেডিট একইভাবে কাজ করে: পেমেন্ট বা ক্রেডিটের টোটাল আলাদা রোতে রাখুন, এবং প্রতিটি ইনভয়েসে কতটা প্রয়োগ হয়েছে তা অ্যালোকেশন রোতে রাখুন।
ডাবল কাউন্টিং প্রতিরোধের পোস্টিং নিয়ম
এই নিয়মগুলো বেশিরভাগ “রহস্য পার্থক্য” দূর করে:
- কখনো নেগেটিভ পেমেন্ট সংরক্ষণ করবেন না। রিফান্ড রেকর্ড ব্যবহার করুন।
- একটি ইনভয়েস পোস্ট করার পরে কখনো তার টোটাল কমাবেন না। ক্রেডিট মেমো বা অ্যাডজাস্টমেন্ট ব্যবহার করুন।
- ডকুমেন্টগুলো একবার পোস্ট করুন (
posted_atটাইমস্ট্যাম্প) এবং পোস্ট করার পর পরিমাণ এডিট করবেন না। - ইনভয়েস ব্যালান্সে পরিবর্তন কেবল পোস্ট করা অ্যালোকেশনগুলোর যোগফল দ্বারা হবে।
- একটি রাইট-অফ হল একটি রিজন কোডসহ অ্যাডজাস্টমেন্ট, যা ইনভয়েসে ক্রেডিটের মতো অ্যালোকেট করা হয়।
ট্যাক্স, ফি, মুদ্রা, এবং রাউন্ডিং
বেশিরভাগ রিকনসিলিয়েশন সমস্যা শুরু হয় এমন টোটাল থেকে যা আপনি পুনর্নির্মাণ করতে পারেন না। সবচেয়ে নিরাপদ নিয়ম সহজ: বিল তৈরি করা লাইনের কাঁচা ডেটা স্টোর করুন, এবং সেই সঙ্গে কাস্টমারকে আপনারা যা দেখিয়েছেন তা টোটাল হিসেবে সংরক্ষণ করুন।
ট্যাক্স ও ফি: এগুলো লাইন-স্তরে রাখুন
ট্যাক্স ও ফি প্রতিটি লাইন আইটেমে সংরক্ষণ করুন, কেবল ইনভয়েস-লেভেল সারমর্ম হিসেবে নয়। বিভিন্ন পণ্যে ভিন্ন কর রেট থাকতে পারে, ফি ট্যাক্সযোগ্য বা না-ও হতে পারে, এবং ছাড় কেবল ইনভয়েসের অংশের উপর প্রযোজ্য হতে পারে। কেবল একটি tax_total রাখলে অবশেষে এমন একটি ক্ষেত্রে পৌঁছাবেন যা ব্যাখ্যা করা যাবে না।
রাখুন:
- কাঁচা লাইনসমূহ (কি বিক্রি হয়েছে, qty, unit price, discount)
- হিসাব করা লাইন টোটাল (line_subtotal, line_tax, line_total)
- ইনভয়েস সারমারি টোটাল (subtotal, tax_total, total)
- ব্যবহৃত ট্যাক্স রেট ও ট্যাক্স টাইপ
- ফি গুলো আলাদা লাইন আইটেম হিসেবে (যেমন, “Payment processing fee”)
এটি ফাইন্যান্সকে টোটালগুলো পুনর্নির্মাণ করার সুযোগ দেয় এবং নিশ্চিত করে ট্যাক্স প্রতিবার একভাবে হিসাব করা হয়েছে।
মাল্টি-মুদ্রা: যা ঘটেছিল এবং আপনি কিভাবে রিপোর্ট করবেন উভয়ই রাখুন
যদি আপনি একাধিক মুদ্রা সমর্থন করেন, প্রতিটি টাকার ডকুমেন্টে লেনদেন মুদ্রা এবং রিপোর্টিং মুদ্রার মান উভয়ই রেকর্ড করুন। ন্যূনতমভাবে রাখুন: প্রতিটি আর্থিক ডকুমেন্টে currency_code, পোস্টিং সময় ব্যবহৃত fx_rate, এবং আলাদা রিপোর্টিং পরিমাণ (যেমন amount_reporting) যদি আপনার বই এক মুদ্রায় ক্লোজ করে।
উদাহরণ: একটি কাস্টমারকে 100.00 EUR + 20.00 EUR VAT বিল করা হলে ঐ EUR লাইন ও টোটালগুলো স্টোর করুন, সঙ্গে fx_rate ও কনভার্টেড রিপোর্টিং টোটালও রাখুন।
রাউন্ডিং আলাদা বিবেচ্য বিষয়া। একটি রাউন্ডিং রুল (প্রতি লাইন বা প্রতি ইনভয়েস) বেছে নিন এবং তাকে মেনে চলুন। যখন রাউন্ডিং পার্থক্য তৈরি করে, তা একটি রাউন্ডিং অ্যাডজাস্টমেন্ট লাইন হিসেবে স্পষ্টভাবে রেকর্ড করুন না করে গোপনভাবে টোটাল বদলাবেন না।
স্ট্যাটাস, পোস্টিং তারিখ, এবং কী না রাখবেন
রিকনসিলিয়েশন জটিল হয়ে যায় যখন “স্ট্যাটাস” কে অ্যাকাউন্টিং ট্রুথের শর্টকাট হিসেবে ব্যবহার করা হয়। স্ট্যাটাসকে ওয়ার্কফ্লো লেবেল হিসেবে রাখুন, আর পোস্ট করা লেজার এন্ট্রিগুলোকে সোর্স অব ট্রুথ হিসেবে বিবেচনা করুন।
স্ট্যাটাসগুলো কঠোর ও নীরস রাখুন। প্রতিটি স্ট্যাটাসের প্রশ্নের উত্তর দিতে হবে: কি এই ডকুমেন্ট এখন টোটালে প্রভাব ফেলতে পারে?
- Draft: অভ্যন্তরীণ, পোস্ট করা নয়, রিপোর্টে পড়বে না
- Issued: ফাইনাল ও পাঠানো, পোস্ট করার জন্য প্রস্তুত (বা ইতিমধ্যে পোস্ট করা)
- Void: বাতিল; যদি পোস্ট করা হয়ে থাকে, তাহলে রিভার্স করা লাগবে
- Paid: পোস্ট করা পেমেন্ট ও ক্রেডিট দ্বারা সম্পূর্ণ নিষ্পন্ন
- Refunded: টাকা আউটগোয়িং রিফান্ড হিসেবে পোস্ট করা হয়েছে
তারিখগুলো অনেক দলের চেয়েও বেশি গুরুত্বপূর্ণ। ফাইন্যান্স জিজ্ঞেস করবে, “এটি কোন মাসে গিয়েছে?” এবং আপনার উত্তর UI লগগুলোর উপর নির্ভর করা উচিত নয়।
issued_at: ইনভয়েস কখন চূড়ান্ত হয়েছিলposted_at: কখন এটি অ্যাকাউন্টিং রিপোর্টে গণ্য হয়settled_at: যখন তহবিল ক্লিয়ার বা পেমেন্ট নিশ্চিত হয়voided_at/refunded_at: কখন রিভার্সাল কার্যকর হয়েছিল
কী না রাখা উচিত: Derived নম্বর যা লেজার থেকে পুনর্নির্মাণ করা যায় না। balance_due, is_overdue, এবং customer_lifetime_value ক্যাশড ভিউ হিসেবে রাখা যায় তবে জরুরি যে আপনি সব সময় এগুলো ইনভয়েস, পেমেন্ট, ক্রেডিট, অ্যালোকেশন, এবং অ্যাডজাস্টমেন্ট থেকে পুনর্নির্মাণ করতে পারেন।
একটি ছোট উদাহরণ: একটি পেমেন্ট রিট্রাই আপনার গেটওয়েতে দুইবার হিট করে। যদি আপনার কাছে idempotency কী না থাকে, আপনি দুইটি পেমেন্ট স্টোর করবেন, ইনভয়েসকে “paid” মার্ক করবেন, তখন ফাইন্যান্স অতিরিক্ত $100 দেখবে। প্রতিটি বাহ্যিক চার্জ চেষ্টা করার জন্য একটি ইউনিক idempotency_key স্টোর করুন এবং ডাটাবেস স্তরে ডুপ্লিকেট অস্বীকার করুন।
ফাইন্যান্স যে রিপোর্টগুলো প্রথম দিন থেকেই আশা করবে
একটি বিলিং লেজার স্কিমা নিজেকে প্রমাণ করে যখন ফাইন্যান্স সহজ প্রশ্নগুলো দ্রুত উত্তর পায় এবং প্রতি বার একই টোটাল পায়। বেশিরভাগ দল শুরু করে:
- Accounts receivable aging: কাস্টমার ও এজ বাল্কেট (0-30, 31-60 ইত্যাদি) অনুযায়ী এখনও খোলা পরিমাণ
- Cash received: পেমেন্ট পোস্টিং তারিখের ভিত্তিতে দিন, সপ্তাহ, মাস অনুযায়ী সংগৃহীত টাকা
- Revenue vs cash: ইনভয়েস পোস্টিং বনাম পেমেন্ট পোস্টিং
- Audit trail for exports: একটি GL এক্সপোর্ট লাইনের থেকে সঠিক ডকুমেন্ট ও অ্যালোকেশন রো পর্যন্ত ড্রিল-ব্যাক
Aging-এ অ্যালোকেশন সবচেয়ে বেশি গুরুত্ব রাখে। Aging মানে নয় “ইনভয়েস টোটাল minus পেমেন্ট টোটাল।” এটি হল “কোন তারিখ অনুযায়ী প্রতিটি ইনভয়েসে কত ওপেন আছে।” এজন্য স্টোর করা দরকার প্রতিটি পেমেন্ট/ক্রেডিট/অ্যাডজাস্টমেন্ট কোন ইনভয়েসে কতটা প্রয়োগ হয়েছে এবং সেই অ্যালোকেশন কখন পোস্ট করা হয়েছে তা।
Cash received পেমেন্ট টেবিল দ্বারা চালিত হওয়া উচিত, ইনভয়েস স্ট্যাটাস দ্বারা নয়। কাস্টমার আগে, পরে বা অংশিকভাবে পেতে পারে।
Revenue vs cash বুঝায় কেন ইনভয়েস এবং পেমেন্ট আলাদা রাখা জরুরি। উদাহরণ: আপনি 30 মার্চ $1,000 ইনভয়েস জারি করেন, 5 এপ্রিল $600 পান, এবং 20 এপ্রিল $100 ক্রেডিট দেন। রেভেন্যু মার্চে যায় (ইনভয়েস পোস্টিং), ক্যাশ এপ্রিল (পেমেন্ট পোস্টিং), এবং ক্রেডিট রিসিভেবল কমায় যখন পোস্ট করা হয়। অ্যালোকেশনগুলো এগুলোকে টাইট করে।
উদাহরণ পরিস্থিতি: এক কাস্টমার, চারটি ডকুমেন্ট টাইপ
এক কাস্টমার, এক মাস, চারটি ডকুমেন্ট টাইপ। প্রতিটি ডকুমেন্ট একবার সংরক্ষিত হয়, এবং টাকা একটি অ্যালোকেশন টেবিলের মাধ্যমে প্রবাহিত হয় (যাকে কখনো কখনো “applications” বলা হয়)। এতে চূড়ান্ত ব্যালান্স পুনর্গণনা করা সহজ এবং অডিটযোগ্য।
ধরা যাক কাস্টমার C-1001 (Acme Co.)।
আপনি যে রেকর্ডগুলো তৈরি করবেন
invoices
| invoice_id | customer_id | invoice_date | posted_at | currency | total |
|---|---|---|---|---|---|
| INV-10 | C-1001 | 2026-01-05 | 2026-01-05 | USD | 120.00 |
payments
| payment_id | customer_id | received_at | posted_at | method | amount |
|---|---|---|---|---|---|
| PAY-77 | C-1001 | 2026-01-10 | 2026-01-10 | card | 70.00 |
credits (ক্রেডিট মেমো, goodwill credit, ইত্যাদি)
| credit_id | customer_id | credit_date | posted_at | reason | amount |
|---|---|---|---|---|---|
| CR-5 | C-1001 | 2026-01-12 | 2026-01-12 | service issue | 20.00 |
adjustments (পরে করা সংশোধন, নতুন বিক্রি নয়)
| adjustment_id | customer_id | adjustment_date | posted_at | note | amount |
|---|---|---|---|---|---|
| ADJ-3 | C-1001 | 2026-01-15 | 2026-01-15 | underbilled fee | 5.00 |
allocations (এটাই ব্যালান্স রিকনসাইল করে)
| allocation_id | doc_type_from | doc_id_from | doc_type_to | doc_id_to | posted_at | amount |
|---|---|---|---|---|---|---|
| AL-900 | payment | PAY-77 | invoice | INV-10 | 2026-01-10 | 70.00 |
| AL-901 | credit | CR-5 | invoice | INV-10 | 2026-01-12 | 20.00 |
ইনভয়েস ব্যালান্স কিভাবে গণনা করা হয়
INV-10-এর জন্য একটি অডিটর সোর্স রো থেকে ওপেন ব্যালান্স পুনর্গণনা করতে পারবে:
open_balance = invoice.total + sum(adjustments) - sum(allocations)
অতএব: 120.00 + 5.00 - (70.00 + 20.00) = 35.00 দিতে হবে।
“35.00” ট্রেস করতে:
- ইনভয়েস টোটাল (INV-10) থেকে শুরু করুন
- একই ইনভয়েসে পোস্ট করা অ্যাডজাস্টমেন্ট যোগ করুন (ADJ-3)
- ইনভয়েসে প্রয়োগ করা প্রতিটি পোস্ট করা অ্যালোকেশন বাদ দিন (AL-900, AL-901)
- নিশ্চিত করুন প্রতিটি অ্যালোকেশন বাস্তব সোর্স ডকুমেন্ট (PAY-77, CR-5) নির্দেশ করে
- টাইমলাইন ব্যাখ্যা করার জন্য
posted_atও তারিখ যাচাই করুন
রিকনসিলিয়েশন ভাঙানো সাধারণ ভুলগুলো
অধিকাংশ রিকনসিলিয়েশন সমস্যা “গণিতের বাগ” নয়। এগুলো নিয়মের অভাব; ফলে একই বাস্তব ঘটনার বিভিন্ন পথে রেকর্ড হয়ে যায়।
একটি সাধারণ ফাঁদ হল নেগেটিভ রো ব্যবহার করা সংক্ষিপ্তভাবে। একটি নেগেটিভ ইনভয়েস লাইন, একটি নেগেটিভ পেমেন্ট, এবং একটি নেগেটিভ ট্যাক্স লাইন সব ভিন্ন অর্থ বহন করতে পারে। যদি আপনি নেগেটিভ অনুমোদন দেন, একটি কড়া রিভার্সাল নীতি (উদাহরণ: কেবল রিভার্সাল রো ব্যবহার করুন যা মূল রোকে রেফার করে, এবং রিভার্সাল সেমান্টিককে ডিসকাউন্টের সাথে মিশাবেন না) নির্ধারণ করুন।
আরেকটি সাধারণ কারণ হলো ইতিহাস পরিবর্তন করা। যদি একটি ইনভয়েস ইস্যু করা হয়, পরে সেটিকে মুল্য বা ঠিকানার কারণে এডিট করবেন না। আসল ডকুমেন্ট রাখুন এবং একটি অ্যাডজাস্টমেন্ট বা ক্রেডিট ডকুমেন্ট পোস্ট করুন যা পরিবর্তন ব্যাখ্যা করে।
সেগুলোই প্যাটার্ন সাধারণত টোটাল ভেঙে দেয়:
- কঠোর রিভার্সাল রুল ও মূল রোতে রেফারেন্স না থাকা সত্ত্বেও নেগেটিভ রো ব্যবহার করা
- ইস্যু করার পর পুরনো ইনভয়েস এডিট করা পরিবর্তে অ্যাডজাস্টমেন্ট বা ক্রেডিট নোট পোস্ট করা না
- গেটওয়ে ট্রানজেকশন ID গুলোকে ইন্টারনাল ID-র সাথে মেপা ছাড়া একসাথে ব্যবহার করা
- অ্যাপ্লিকেশন কোডকে টোটাল হিসাব করতে দেওয়া কিন্তু সহায়ক রো (ট্যাক্স, ফি, রাউন্ডিং, অ্যালোকেশন) অনুপস্থিত রাখা
- “টাকা এগিয়েছে” (ক্যাশ মুভমেন্ট) এবং “টাকা কোন ইনভয়েসে প্রয়োগ হয়েছে” (অ্যালোকেশন) আলাদা না রাখা
শেষ পয়েন্টটাই সবচেয়ে বিভ্রান্তি সৃষ্টি করে। উদাহরণ: একটি কাস্টমার $100 পে করে এবং আপনি $60 Invoice A তে এবং $40 Invoice B তে প্রয়োগ করেন। পেমেন্ট হল এক ক্যাশ মুভমেন্ট, কিন্তু দুটি অ্যালোকেশন তৈরি করে। যদি আপনি কেবল “payment = invoice” স্টোর করেন, পার্শিয়াল পেমেন্ট, ওভারপেমেন্ট, বা পুনরায় অ্যালোকেশন সমর্থন করতে পারবেন না।
চেকলিস্ট ও পরবর্তী ধাপ
অধিক ফিচার যোগ করার আগে নিশ্চিত করুন বুনিয়াদগুলো কাজ করে। একটি বিলিং লেজার স্কিমা তখনই রিকনসাইল করে যখন প্রতিটি টোটাল নির্দিষ্ট রোতে ট্রেস করা যায় এবং প্রতিটি পরিবর্তনের একটি অডিট ট্রেইল আছে।
দ্রুত রিকনসিলিয়েশন চেকসমূহ
একটি ছোট স্যাম্পল (এক কাস্টমার, এক মাস) এবং তারপর পুরো ডেটাসেটে এই চেকগুলো চালান:
- রিপোর্টে থাকা প্রতিটি পোস্ট করা নম্বর সোর্স রো (ইনভয়েস লাইন, পেমেন্ট, ক্রেডিট মেমো, অ্যাডজাস্টমেন্ট) থেকে ট্রেস করা যায় এবং পোস্টিং তারিখ ও মুদ্রা আছে।
- অ্যালোকেশন কোনো ডকুমেন্টের মোটের চেয়ে বেশি নয় (পেমেন্ট অ্যালোকেশন টোটাল <= পেমেন্ট টোটাল; ক্রেডিটের ক্ষেত্রেও একই)।
- কিছুই মুছে ফেলা হয় না। ভুল এন্ট্রিগুলো রিভার্স করা হয় একটি রিজন সহ, তারপর একটি নতুন পোস্ট করা রো দিয়ে সঠিক করা হয়।
- ওপেন ব্যালান্স ডেরাইভেবল, সংরক্ষিত নয় (ইনভয়েস ওপেন অ্যামাউন্ট = ইনভয়েস টোটাল − পোস্ট করা অ্যালোকেশন ও ক্রেডিট)।
- ডকুমেন্ট টোটাল তাদের লাইনের সাথে মেলে (ইনভয়েস হেডার টোটাল লাইন, ট্যাক্স, এবং ফি’র যোগফলের সমান আপনার রাউন্ডিং রুল অনুযায়ী)।
ব্যবহারিক পরবর্তী ধাপ দ্রুত পাঠানোর জন্য
আপনার স্কিমা দৃঢ় হলে অপারেশনাল ওয়ার্কফ্লো তৈরি করুন:
- অ্যাডমিন স্ক্রীন যা ইনভয়েস, পেমেন্ট, ক্রেডিট, এবং অ্যাডজাস্টমেন্ট তৈরি, পোস্ট এবং রিভার্স করার জন্য প্রয়োজনীয় নোটসহ
- একটি রিকনসিলিয়েশন ভিউ যা ডকুমেন্ট ও অ্যালোকেশন এক পাশে রেখে দ্রুত পর্যালোচনার মত দেখায়, কারা কখন পোস্ট করেছে তা সহ
- ফাইন্যান্সের প্রত্যাশিত এক্সপোর্ট (পোস্টিং তারিখ, কাস্টমার, GL ম্যাপিং ইত্যাদি অনুযায়ী)
- পিরিয়ড ক্লোজ ওয়ার্কফ্লো: ক্লোজ করা মাসের জন্য পোস্টিং তারিখ লক করা এবং লেট ফিক্সের জন্য রিভার্সাল এন্ট্রির প্রয়োজন
- টেস্ট স্যেনারিও: রিফান্ড, পার্শিয়াল পেমেন্ট, রাইট-অফ ইত্যাদি টেস্ট করুন এবং প্রত্যাশিত টোটালের সাথে মেলে কিনা যাচাই করুন
যদি আপনি দ্রুত একটি ব্যবহারযোগ্য অভ্যন্তরীণ ফাইন্যান্স পোর্টাল চালু করতে চান, AppMaster (appmaster.io) আপনাকে PostgreSQL স্কিমা মডেল করতে, API জেনারেট করতে, এবং একই সোর্স থেকে অ্যাডমিন স্ক্রীন তৈরি করতে সাহায্য করতে পারে, যাতে পোস্টিং এবং অ্যালোকেশন নিয়ম অ্যাপ বিকাশের সঙ্গে সঙ্গত থাকে।
প্রশ্নোত্তর
রিকনসিলিয়েশন মানে রিপোর্টে দেখানো প্রতিটি মোট সংখ্যা स्रोत রেকর্ড থেকে পুনর্নির্মাণযোগ্য হওয়া এবং প্রতিটি মানের পেছনে তারিখভিত্তিক এন্ট্রিতে ট্রেস করা। যদি আপনার রিপোর্ট বলে যে আপনি $12,430 সংগ্রহ করেছেন, তাহলে আপনাকে সেই নির্দিষ্ট পোস্ট করা পেমেন্ট ও রিফান্ডগুলো দেখাতে হবে যেগুলো মিলিয়ে ঐ সংখ্যাটি হয়, এবং তা ওভাররাইট করা ফিল্ডে ভর করে নয়।
সবচেয়ে সাধারণ কারণ হল পরিবর্তনশীল “ফলাফল” ফিল্ডগুলো (যেমন paid_amount বা balance_due) যাতে ইতিহাস হিসেবে সংরক্ষণ করা হয়। যদি এই ফিল্ডগুলো রিট্রাই, বাগ, বা ম্যানুয়াল এডিট দিয়ে বদলে যায়, আপনি ঐতিহাসিক ট্রেইল হারাবেন এবং মোটগুলো বাস্তবে কী ঘটেছে তার সাথে মেলে না।
প্রতিটি রেকর্ড ভিন্ন বাস্তব ঘটনাকে প্রতিনিধিত্ব করে এবং ভিন্ন হিসাব-অর্থনৈতিক অর্থ বহন করে। যদি সবকিছুকে একটা একক “transaction” টেবিলে ঠেলে দেয়া হয় এবং ঐ টিতে অনেক অপশনাল ফিল্ড রাখা হয়, তাহলে রিপোর্ট অনুমান বনে যায় এবং অডিটে বিতর্ক জন্মায় যে ওই রো আসলে কী বোঝায়।
একটি ক্রেডিট মেমো কাস্টমারের দায় কমায় কিন্তু আর্থিকভাবে টাকা পাঠায় না। একটি রিফান্ড হল টাকা আপনার কাছ থেকে বেরোনো — সাধারণত পূর্বের কোনো পেমেন্টের সাথে যুক্ত। এগুলোকে একই জিনিস ধরে নেয়া বা নেগেটিভ পেমেন্ট হিসেবে রেকর্ড করলে ক্যাশ রিপোর্টিং ও ব্যাংক ম্যাচিং জটিল হয়ে পড়ে।
ইতিহাস লেখার পরিবর্তে রিভার্সাল পোস্ট করুন। মূল এন্ট্রির সাথে বিপরীত চিহ্নের নতুন এন্ট্রি তৈরি করুন এবং সেটাকে মূল পোস্টিংয়ের সাথে লিংক করুন, তারপর সঠিক অ্যালোকেশন পোস্ট করুন — এতে অডিট ট্রেইল দেখায় কী বদলানো হয়েছে এবং কেন।
পেমেন্ট বা ক্রেডিটকে এক বা একাধিক ইনভয়েসে যুক্ত করার জন্য স্পষ্ট অ্যালোকেশন রেকর্ড (applications) ব্যবহার করুন — প্রতিটি অ্যালোকেশন থাকবে allocated_amount এবং posted_at সহ। ইনভয়েসের ওপেন ব্যাল্যান্স ইনভয়েস টোটাল + সমন্বয় − পোস্ট করা অ্যালোকেশন থেকে হিসাবযোগ্য হওয়া উচিত।
উভয় তারিখই রাখুন: ডকুমেন্ট তারিখ ও পোস্টিং তারিখ। ডকুমেন্ট তারিখ হলো কাস্টমারকে যা দেখানো হয়, আর পোস্টিং তারিখ নিয়ন্ত্রণ করে কখন এটি ফাইন্যান্স রিপোর্টে পড়ে—তাই মাস-অবধি রিপোর্ট UI লোগ থেকে নাও আসা উচিত।
ট্যাক্স ও ফি লাইন-স্তরে রাখুন, এবং ক্লায়েন্টকে দেখানো সঠিক টোটালগুলোও সংরক্ষণ করুন। শুধুমাত্র ইনভয়েস-লেভেল tax_total রাখলে ভিন্ন ভিন্ন ট্যাক্স রেট বা ছাড়ের ক্ষেত্রে পুনরায় তৈরি করা সমস্যায় পড়তে হবে।
লেনদেনের মুদ্রায় পরিমাণ এবং রিপোর্টিং মুদ্রায় কনভার্টেড পরিমাণ উভয়ই সংরক্ষণ করুন, এবং পোস্টিং সময় যে FX রেট ব্যবহার করা হয়েছে সেটাও রাখুন। রাউন্ডিং নীতিটি একবার নির্ধারণ করুন (প্রতি লাইন বা প্রতি ইনভয়েস) এবং রাউন্ডিং থেকে হওয়া পার্থক্য স্পষ্টভাবে একটি রাউন্ডিং অ্যাডজাস্টমেন্ট হিসেবে রেকর্ড করুন।
স্ট্যাটাসকে শুধুই ওয়ার্কফ্লো লেবেল হিসেবে ব্যবহার করুন (Draft, Issued, Void, Paid ইত্যাদি)। অ্যাকাউন্টিং ট্রুথ হিসেবে পোস্ট করা লেজার এন্ট্রিগুলো এবং অ্যালোকেশন গণ্য করুন—স্ট্যাটাস ভুল হতে পারে, কিন্তু অপরিবর্তনীয় পোস্ট করা এন্ট্রি ফাইনান্সকে একইভাবে সব সময় পুনর্নির্মাণ করতে দেবে।


