বহুমুদ্রার সাবস্ক্রিপশন ইনভয়েসিং: ওয়েব, মোবাইল ও একাউন্টিং এক্সপোর্টে মোটগুলো কনসিস্টেন্ট রাখার জন্য ব্যবহারিক রাউন্ডিং ও মাইনিমাল টেবিল পদ্ধতি।

একটি সাধারণ মাথাব্যথা: ওয়েব চেকআউট একটি মোট দেখায়, মোবাইল অ্যাপ সামান্য ভিন্ন মোট দেখায়, এবং একাউন্টিং এক্সপোর্ট তৃতীয় একটি সংখ্যা দেয়। প্রতিটি সিস্টেম "যথোচিত" গাণিতিক কাজ করছে, কিন্তু একরকম করে না।
সাবস্ক্রিপশনগুলো এই সমস্যা আরও বাড়িয়ে তোলে কারণ আপনি একই গণনা বারবার করেন। ছোট পার্থক্যগুলো সময়ের সাথে জমানো হয়—রিনিউয়াল, মাঝ-মাসে আপগ্রেডের প্রোরেশন, ক্রেডিট ও রিফান্ড, ব্যর্থ পেমেন্ট পরে রিট্রাই চার্জ, এবং প্ল্যানের শুরু বা শেষের আংশিক সময়—সব জায়গায়।
ড্রিফট সাধারণত ক্ষুদ্র পছন্দ থেকে শুরু হয় যা দেখা যায় না যতক্ষণ না সমস্যা হয়: কখন রাউন্ড করবেন (প্রতি লাইন না শেষে), কোন ট্যাক্স বেস ব্যবহার হবে (নেট বনাম গ্রস), কোন মুদ্রাগুলো 0 বা 3 দশমিক মাইনর ইউনিট আছে, এবং কোন FX রেট প্রয়োগ করা হচ্ছে (কোন টাইমস্ট্যাম্প, কোন উৎস, কী প্রিসিশন)। যদি ওয়েব প্রতি লাইনে 2 দশমিক রাউন্ড করে এবং মোবাইল শুধুমাত্র চূড়ান্ত মোট রাউন্ড করে, একই ইনপুট থেকেও 0.01 পার্থক্য দেখা দিতে পারে।
লক্ষ্য স্পষ্ট কিন্তু নীরস: একই ইনভয়েস প্রতিটি জায়গায়, প্রতিবার একই মোট উত্পন্ন করুক। এটি গ্রাহককে শান্ত রাখে, সাপোর্ট টিকিট কমায় এবং অডিটে টিকে থাকতে সাহায্য করে।
"কনসিস্টেন্ট" মানে একটি ইনভয়েস আইডি ও ভার্সনের ক্ষেত্রে:\n
উদাহরণ: একজন গ্রাহক মাঝ-মাসে EUR 19.99 থেকে EUR 29.99-এ আপগ্রেড করে, প্রোরেটেড চার্জ পায়, তারপর একটু ক্রেডিট পায় ডাউনটাইমের জন্য। যদি একটি সিস্টেম প্রতিটি প্রোরেটেড লাইনে রাউন্ড করে এবং অন্যটি শুধু চূড়ান্ত মোট রাউন্ড করে, এক্সপোর্ট করা ইনভয়েস গ্রাহককে দেখানো সংখ্যার সাথে মিলবে না, যদিও প্রতিটি সংখ্যা "কাছাকাছি" দেখায়।
FX রেট বা ট্যাক্স রাউন্ডিং নিয়ে তর্ক করার আগে বেসিকগুলো ঠিক করুন। এরা যদি অস্পষ্ট থাকে, ইনভয়েসগুলো ওয়েব অ্যাপ, মোবাইল, ও একাউন্টিং এক্সপোর্টে ভাসতে থাকবে।
প্রতিটি ইনভয়েস লাইন ও মোটে তিনটি পরিমাণ স্পষ্টভাবে বহন করা উচিত: নেট (ট্যাক্স ছাড়াই), ট্যাক্স, এবং গ্রস (নেট + ট্যাক্স)। একটি কে স্টোর ও গণনার সোর্স অব ট্রুথ হিসেবে বেছে নিন, তারপর অন্যগুলো সব জায়গায় একইভাবে নির্গত করুন। অনেক দল নেট ও ট্যাক্স স্টোর করে, তারপর গ্রস = নেট + ট্যাক্স গণনা করে রাখে কারণ এভাবে অডিট ও রিফান্ড সহজ হয়।
প্রতিটি সংখ্যার সাথে মুদ্রা স্পষ্টভাবে যুক্ত করুন। দলগুলো প্রায়ই তিনটি ধারণা মিশিয়ে দেয়:
এসব একই হতে পারে, কিন্তু বাধ্যতামূলক নয়। যদি আপনার ইনভয়েস EUR-এ হয় কিন্তু কার্ড USD-এ সেটল হয়, ইনভয়েসকে EUR-এ কনসিস্টেন্ট রাখতে হবে এমনকি ব্যাঙ্ক আমানত ভিন্ন হলে-ও।
পরেরটি: টাকাকে মাইনর ইউনিটে ইন্টেজার হিসেবে বিবেচনা করুন (উদাহরণ: সেন্ট)। 9.99-কে ফ্লো্যাট হিসেবে স্টোর করা পরবর্তীতে 9.989999 ধরনের সমস্যা তৈরি করতে পারে, বিশেষ করে ট্যাক্স, ডিসকাউন্ট, প্রোরেশন বা একাধিক আইটেম যোগ করলে। 999 (সেন্ট) এবং একটি মুদ্রা কোড স্টোর করুন, এবং কেবল ডিসপ্লের সময় ফরম্যাট করুন।
শেষে, আপনার প্রাইসিং ট্যাক্স মোড নির্ধারণ করুন:
একটি বাস্তব পরীক্ষা: একটি প্ল্যান 10.00 (ট্যাক্স-ইনক্লুসিভ, 20% VAT) হিসেবে দেখালে ওয়েব ও মোবাইলে একই স্টোর করা গ্রস মাইনর ইউনিট উত্পন্ন করা উচিত, তারপর নেট ও ট্যাক্স একই শেয়ার্ড নিয়ম দিয়ে নির্ধারিত হবে।
FX পার্থক্যগুলো প্রায়ই ট্যাক্স ও রাউন্ডিং নিয়মের আগে থেকেই শুরু হয়। দুই সিস্টেমই "ঠিক" হতে পারে এবং তবুও ভিন্ন হতে পারে কারণ তারা আলাদা উৎস, আলাদা টাইমস্ট্যাম্প, অথবা আলাদা প্রিসিশন ব্যবহার করেছে।
রেট প্রোভাইডারগুলো সাধারণত একরকম দেয় না। কেউ মিড-মার্কেট রেট দেয়, কেউ স্প্রেড যোগ করে। কেউ প্রতি মিনিট আপডেট করে, কেউ ঘন্টায় বা দৈনিক। এমনকি একই প্রোভাইডার হলেও, একটি সিস্টেম রেটকে 4 দশমিক পর্যন্ত রাউন্ড করে আর অন্যটি 8+ দশমিক রাখলে সাবস্ক্রিপশন পরিমাণ ও ট্যাক্স গুণ করলে টোটালে পরিবর্তন আসে।
সবচেয়ে গুরুত্বপূর্ণ সিদ্ধান্ত হচ্ছে আপনার রেট টাইমস্ট্যাম্পের অর্থ কী। যদি আপনি EUR-এ চার্জ দেন কিন্তু গ্রাহক USD-এ পে করে, আপনি কি ইনভয়েস ইস্যু হলে রেট লক করবেন, না কি পেমেন্ট ক্যাপচার সময় লক করবেন? উভয়ই প্রচলিত, কিন্তু ওয়েব, মোবাইল, ও এক্সপোর্টে মিশালে মিল ও মিলবে না।
একবার নিয়ম বেছে নিলে, ইনভয়েসে আপনি যে সঠিক রেট ব্যবহার করেছেন তা স্টোর করুন। পরে "বর্তমান" রেট থেকে পুনরায় গণনা করবেন না, যদিও ঐতিহাসিক রেট খোঁজা যায়। প্রোভাইডারের করেকশন, টাইমজোন পার্থক্য, এবং ক্ষুদ্র প্রিসিশন পরিবর্তন পুরনো ইনভয়েসগুলো এক্সপোর্ট বা পুনঃজেনারেটের সময় ড্রিফট করবে।
সরল উদাহরণ: আপনি একটি ইনভয়েস 23:59-এ ইস্যু করেছেন, কিন্তু পেমেন্ট 00:02-এ সফল হয়েছে। ওই টাইমস্ট্যাম্পগুলো প্রোভাইডারের "দিন"-এ পড়তে আলাদা হতে পারে, ফলে দৈনিক রেট টেবিল ভিন্ন সংখ্যা দিতে পারে।
এই FX বিবরণগুলো নির্ধারণ ও ডকুমেন্ট করুন:
বিশেষ কেসগুলো আগে থেকেই হ্যান্ডেল করুন: শূন্য-দশমিক মুদ্রা (যেমন JPY), খুব উচ্চ-প্রিসিশন রেট, এবং রিফান্ড। রিফান্ড সাধারণত মূল ইনভয়েসের স্টোরড FX রেট পুনরায় ব্যবহার করা উচিত; না হলে রিফান্ড গ্রাহক প্রত্যাশা ও একাউন্টিং এক্সপোর্টের সঙ্গে মিলবে না।
আপনি যদি চান ইনভয়েসগুলো ওয়েব, মোবাইল, এবং এক্সপোর্টে মেলে, আপনার ডেটা মডেল ইনপুট নয় ফলাফলগুলো সংরক্ষণ করতে হবে। লক্ষ্য সোজা: একই ইনভয়েস একই মাইনর ইউনিটগুলি অবিশ্যি সব জায়গায় রেন্ডার করতে পারবে, এমনকি কয়েক মাস পরেও।
সামান্য কয়েকটি এনটিটি সাধারণত যথেষ্ট:
কী নিয়ম: টাকা ফিল্ডগুলো মাইনর ইউনিটে পূর্ণসংখ্যা হওয়া উচিত। ইউনিট প্রাইস ও ক্যালকুলেট করা লাইন টোটাল উভয়ই সংরক্ষণ করুন। এতে পরে রাউন্ডিং নিয়ম বা ভিন্ন FX সোর্স দিয়ে পুনরায় হিসাব করা থেকে সমস্যা রোধ হয়।
FX ইনভয়েসে ক্যাপচার করা দরকার, অনুমান করে নয়। আপনি যদি একটি শেয়ারড FX টেবিলও রাখেন, ইনভয়েসে সেই চূড়ান্তকরণ সময়ে ব্যবহৃত fx_rate_value (কোথা থেকে এসেছে সহ) রাখতে হবে যাতে এক্সপোর্টগুলো একই সংখ্যাগুলো পুনরুত্পন্ন করতে পারে।
আপনি কেবল আলাদা ট্যাক্স ব্রেকডাউন টেবিল চাইবেন যখন একটি ইনভয়েসে একাধিক ট্যাক্স রেট বা জুরিসডিকশন একসঙ্গে থাকতে পারে (উদাঃ মিশ্র আইটেম, EU VAT + লোকাল লেভি, বা ঠিকানাভিত্তিক ট্যাক্স পরিবর্তন)। তখন প্রতি ট্যাক্স রেটের জন্য একটি সারি করে taxable_base_minor এবং tax_amount_minor সংরক্ষিত করুন।
শেষে, একটি ফাইনালাইজড ইনভয়েসকে অপরিবর্তনীয় হিসেবে বিবেচনা করুন। চূড়ান্ত মুহূর্তে গণনার স্ন্যাপশট সংরক্ষণ করুন এবং পরে সাবস্ক্রিপশন থেকে মোটগুলো পুনরায় গণনা করবেন না। একটাই সিদ্ধান্ত বেশিরভাগ "কেন সেন্টগুলো বদলে গেল" বাগগুলো দূর করে।
রাউন্ডিং কেবল একটি গাণিতিক বিস্তারিত নয়। এটি একটি প্রোডাক্ট নীতিও। যদি ওয়েব আলাদা ভাবে রাউন্ড করে, মোবাইল আলাদা করে, আর একাউন্টিং এক্সপোর্ট আরও ভিন্ন — তাহলে ইনপুট এক হলেও ভিন্ন মোট পাবেন।
তিনটি সাধারণ কৌশল আছে, এবং এগুলো কোথায় আপনি মাইনর ইউনিট "লক" করবেন সেটা অনুসারে আলাদা:\n
সাবস্ক্রিপশনের জন্য একটি ভালো ডিফল্ট হলো প্রতি লাইনে রাউন্ড। গ্রাহকদের জন্য এটি সোজা (প্রতিটি লাইনের ফলটি যৌক্তিক দেখায়), অডিটে সহজ (প্রতিটি লাইনের টোটাল ব্যাখ্যা করা যায়), এবং রিনিউয়ালে স্থিতিশীল। প্রতি ইউনিটে রাউন্ড করলে পরিমাণ পরিবর্তিত হলে ড্রিফট হতে পারে। কেবল ইনভয়েস টোটালে রাউন্ড করলে প্রায়ই "কেন এই লাইনগুলো মিলে না" ধরনের টিকিট আসে কারণ দৃশ্যমান লাইনের যোগফল প্রদর্শিত মোটের সাথে মিলে না।
ক্লাসিক পেনি সমস্যা দেখা যায় যখন অনেক ছোট আইটেম বা ভগ্নাংশ ট্যাক্স থাকে। উদাহরণ: 20 লাইন প্রতিটিই 0.004 রিমেইন্ডার তৈরি করলে, লাইনে রাউন্ড করলে চূড়ান্তে 0.08 পার্থক্য দেখা দিতে পারে যা কেবল টোটালে রাউন্ড করলে ছিল না। FX রূপান্তরের সঙ্গে এই ক্ষুদ্র রিমেইন্ডারগুলো বেশি ঘন ঘন আসে এবং সময়ের সাথে এক্সপোর্ট ও রেভিনিউ রিপোর্টে জমা হয়।
যেকোন কৌশল বেছে নিন, তা নির্ধারিতভাবে প্রয়োগ করুন:
যদি আপনি ওয়েব ও মোবাইল বিলিং ফ্লো দুটোই তৈরি করেন, রাউন্ডিং নিয়মটি UI আচরণ হিসেবে রাখবেন না — একটি টেস্টেবল স্পেস যেমন স্পেক হিসেবে লিখে রাখুন।
ওয়েব, মোবাইল, এবং একাউন্টিং এক্সপোর্টে একই সংখ্যাগুলো রাখতে গণনাকে একটি রেসিপির মতো আচরণ করুন। মূল ধারণা: উচ্চ প্রিসিশনে গণনা করুন, কিন্তু ইনভয়েস কারেন্সিতে কেবলই পূর্ণসংখ্যা মাইনর ইউনিট সংরক্ষণ ও যোগ করুন।
প্রতিটি লাইন আইটেমের নেট পরিমাণ উচ্চ প্রিসিশনে শুরু করুন। যখন আপনি পরিমাণ দিয়ে গুণ, ডিসকাউন্ট প্রয়োগ, এবং প্রয়োজনে মুদ্রা রূপান্তর করবেন তখন অতিরিক্ত দশমিক রাখুন। তারপর আপনার বেছে নেওয়া নিয়ম দিয়ে ইনভয়েস কারেন্সি মাইনর ইউনিটে একবার রাউন্ড করুন। সেই ইন্টেজারটি লাইন নেট হিসেবে স্টোর করুন।
স্টোর করা লাইন নেট থেকে ট্যাক্স গণনা করুন (বা যদি আপনার নিয়ম গ্রুপিং অনুমোদন করে তাহলে ট্যাক্স গ্রুপ সাবটোটাল থেকে)। একই রাউন্ডিং নিয়ম প্রয়োগ করে ট্যাক্সকে মাইনর ইউনিটে স্টোর করুন। এখানেই সিস্টেমগুলো প্রায়ই ড্রিফট করে: একপাশে হয় রাউন্ডিং ট্যাক্সের আগে, অন্যপাশে পরে।
প্রতিটি লাইনের গ্রস হিসাব করুন (স্টোর করা নেট + স্টোর করা ট্যাক্স)। ইনভয়েস টোটালগুলো হল সেভ করা মাইনরগুলোর যোগফল। ডিসপ্লে বা এক্সপোর্টের সময় ভাসমান-বিন্দু থেকে মোটগুলো পুনরায় গণনা করবেন না। প্রদর্শনী ও এক্সপোর্টগুলো স্টোর করা ইন্টেজারগুলো পড়ে ফরম্যাট করবে।
যদি আপনার স্থানীয় নিয়ম ইনভয়েস-লেভেলে ট্যাক্স টোটাল প্রয়োজন করে, তাহলে আপনাকে একটি রিমেইন্ডার বিতরণ করতে হতে পারে। উদাহরণ: তিনটি লাইনে প্রতিটিতে 0.01 ট্যাক্স হলে মোট 0.03, কিন্তু ইনভয়েস-লেভেল রাউন্ডিং বলে 0.02। একটি ডিটারমিনিস্টিক টাই-ব্রেকার সিদ্ধান্ত নিন (উদাহরণ: সবচেয়ে বড় ট্যাক্সেবেল লাইনের থেকে 1 মাইনর ইউনিট যোগ বা বিয়োগ করা, তারপর লাইন আইডি অনুসারে স্থিতিশীল সাজানো)। ক্ষুদ্র ট্যাক্স সমন্বয়কে প্রভাবিত লাইনে স্টোর করুন যাতে প্রতিটি সিস্টেম এটি পুনরুত্পাদন করতে পারে।
ইনভয়েস লক করুন। চূড়ান্ত রাউন্ডিং ও যেকোন রিমেইন্ডার বিতরণ শেষে ইনভয়েসকে অপরিবর্তনীয় মনে করুন। পরে সাবস্ক্রিপশন প্রাইস পরিবর্তন হলে নতুন ইনভয়েস বা ক্রেডিট নোট তৈরি করুন, পুরনো সংখ্যাগুলো কখনো পুনরায় লেখবেন না।
একটি বাস্তব পরীক্ষা: EUR 9.99 প্ল্যান যদি 19% VAT থাকে, আপনার স্টোর করা নেট হতে পারে 999 সেন্ট, ট্যাক্স 190 সেন্ট, গ্রস 1189 সেন্ট। প্রতিটি ক্লায়েন্ট ওই স্টোর করা ইন্টেজারগুলো থেকে 11.89 EUR রেন্ডার করবে, অন-ফ্লাই VAT পুনরায় গণনা করে নয়।
ট্যাক্স রাউন্ডিংই সেই জায়গা যেখানে সঠিক গণিত ভিন্ন ইনভয়েসে পরিণত হয়। মুল বিষয়ে: আগে রাউন্ড করলে চূড়ান্ত সমষ্টি বদলে যায়।
আপনি যদি প্রতি লাইনে (বা প্রতি পরিমাণে) ট্যাক্স রাউন্ড করে তারপর যোগ করুন, তাহলে আপনি ভিন্ন ফল পাবেন যদি আপনি ইনভয়েস-লেভেলে অরাউন্ডেড ট্যাক্স যোগ করে শেষে রাউন্ড করেন। অনেক লাইনে-gap টাকি জমে যায়, বিশেষ করে মাইনর ইউনিট ও FX রূপান্তর ইতিমধ্যে ভগ্নাংশ তৈরি করলে।
নির্দিষ্ট উদাহরণ (2 দশমিক): দুই লাইন প্রতিটির ট্যাক্সেবল অ্যামাউন্ট 0.05 এবং ট্যাক্স 10%। লাইনে অনরাউন্ডেড ট্যাক্স 0.005। যদি আপনি প্রতি লাইনে রাউন্ড করেন, প্রতিটি হয়ে যায় 0.01, মোট ট্যাক্স 0.02। কিন্তু ইনভয়েস-লেভেলে রাউন্ড করলে মোট ট্যাক্স হবে 0.01। উভয়ই যুক্তিযুক্ত—তবে একে অপরের সাথে মিলবে না।
যখন আপনাকে প্রতিটি লাইনের ট্যাক্স দেখাতে হবে কিন্তু ইনভয়েস মোটও এক্সাক্ট থাকতে হবে, রাউন্ডিং রিমেইন্ডার নির্দিষ্টভাবে বরাদ্দ করুন:
এক্সপোর্টগুলো এখনও ড্রিফট করতে পারে যখন অ্যাকাউন্টিং লাইনগুলো গ্রুপ করে (প্রোডাক্ট, ট্যাক্স রেট, বা জুরিসডিকশনের ভিত্তিতে)। যাতে এক্সপোর্ট টোটাল ইনভয়েস টোটালের সঙ্গে মিলে, প্রতিটি প্রয়োজনীয় গ্রুপের ভিতরে প্রথমে রিমেইন্ডার বরাদ্দ করুন, তারপর নিশ্চিত করুন যে গ্রুপ টোটালগুলো মিলে ইনভয়েস ট্যাক্স ও গ্রস-এর সাথে।
যদি একাউন্টিং টাক্স রেট বা জুরিসডিকশনের ভিত্তিতে ভাগ চাই কিন্তু UI একটাই ট্যাক্স দেখায়, তবুও ব্রেকডাউন সংরক্ষণ করুন (প্রতি রেট বা জুরিসডিকশনের টোটালস সহ) এবং একটি অডিট-ফ্রেন্ডলি বরাদ্দ নিয়ম রাখুন। UI মাত্র একটিই টোটাল দেখাতে পারে, এক্সপোর্টগুলো বিশদ বকেট নিয়ে যাবে কিন্তু ইনভয়েস গ্র্যান্ড টোটাল পরিবর্তন করবে না।
বেশিরভাগ ইনভয়েস মিল না করা করণ কোণের নিয়মে ঘটে। নিয়মগুলো আগে থেকেই নির্ধারণ করুন এবং তারা অবাক করা বন্ধ করবে।
শূন্য-দশমিক মুদ্রাগুলো বিশেষ যত্ন চাই। JPY ও KRW-এর মতো মুদ্রায় মাইনর ইউনিট নেই, তাই যেকোন ধাপ যেটি "সেন্ট" ধরে নেয় চুপচাপ পার্থক্য সৃষ্টি করবে। প্রতি লাইন, ট্যাক্স লেভেল, বা কেবল চূড়ান্ত টোটাল কোথায় রাউন্ড হবে তা ঠিক করুন এবং প্রতিটি ক্লায়েন্টে একই মুদ্রা সেটিংস নিশ্চিত করুন।
ক্রস-বর্ডার VAT/GST গ্রাহক লোকেশনের উপর ট্যাক্স রেট পরিবর্তন করতে পারে এবং কোন প্রমাণ আপনি নেবেন তার উপরও নির্ভর করে (বিলিং ঠিকানা, IP, ট্যাক্স আইডি)। কষ্টকর অংশটি রেট নয়, আপনি কখন সেটি লক করেন। একটি সময় বিন্দু বেছে নিন (চেকআউট, ইনভয়েস ইস্যু তারিখ, বা সার্ভিস পিরিয়ড শুরু) এবং তা বজায় রাখুন।
প্রোরেশনই সেই জায়গা যেখানে ভগ্নাংশগুলো গুণিত হয়। মাঝ-মাস আপগ্রেড এমন অ্যামাউন্ট তৈরি করে যেগুলো দৈনিক হিসেবে 9.3333... হতে পারে। আপনি কি নেট অ্যামাউন্টগুলো প্রোরেট করবেন, গ্রস প্রোরেট করবেন, না কি প্রথমে সার্ভিস পিরিয়ড গুনবেন — এই অর্ডার পরিবর্তন করলে শেষের মাইনর ইউনিট বদলে যায়।
এই নিয়মগুলো লিখে রাখুন যাতে সময়ের সাথে তারা ভাঙে না:\n
রিফান্ডই শেষ ফাঁদ। যদি মূল ইনভয়েসে একটি 0.01 রিমেইন্ডার একটি লাইনে বরাদ্দ করা ছিল, আপনার রিফান্ডেও সেই নির্দিষ্ট বরাদ্দ উল্টে দিতে হবে। নাহলে গ্রাহক এক মোট দেখে এবং আপনার লেজার এক্সপোর্ট অন্য মোট দেখায়।
অনেক ইনভয়েস মিল না হওয়ার কারণ কঠিন গণিত নয়—এগুলো স্ট্যাকের বিভিন্ন অংশে ছোট ছোট অনসামঞ্জস্যপূর্ণ সিদ্ধান্ত।
বড় একটি হলো টাকা ফ্লোট হিসেবে সংরক্ষণ করা। 19.99-এর মতো মান অনেক সিস্টেমে ঠিকভাবে প্রতিনিধিত্ব করা যায় না, তাই লাইন যোগ, ডিসকাউন্ট, বা ট্যাক্স গণনা করলে ক্ষুদ্র ত্রুটি তৈরি হয়। টাকার পরিমাণগুলো মাইনর ইউনিটে ইন্টেজার হিসেবে স্টোর করুন এবং মুদ্রা কোড ও মাইনর ইউনিট স্কেল রাখুন।
আরেকটি সাধারণ সমস্যা হলো এক্সপোর্টের সময় FX পুনরায় গণনা করা। গ্রাহক নির্দিষ্ট রেটে পে করেছে। যদি আপনার এক্সপোর্ট আজকের রেট টেনে নেয়, আপনি ভিন্ন মোট পেতে পারেন যদিও সকল ধাপ সঠিক ছিল। ইনভয়েসকে একটি স্ন্যাপশট হিসেবে বিবেচনা করুন: FX রেট, রূপান্তরিত অ্যামাউন্ট, ও রাউন্ডিং রেজাল্টগুলো স্টোর করুন।
রাউন্ডিং পার্থক্য UI ও ব্যাকএন্ডে আলাদা ধাপে করলেও দেখা যায়। উদাহরণ: ব্যাকএন্ড প্রতি লাইন রাউন্ড করে, ওয়েব UI কেবল ইনভয়েস টোটালে রাউন্ড করে—দুটোই যৌক্তিক তবে মিলবে না।
পাঁচটি সাধারণ অপরাধী সবচেয়ে বেশি গ্যাপ সৃষ্টি করে:\n
সরলতম সমাধান নীরস কিন্তু কার্যকর: ব্যাকএন্ডে একবার গণনা করুন, ইনভয়েস স্ন্যাপশট সংরক্ষণ করুন, এবং ওয়েব ও মোবাইল সেই স্টোর করা সংখ্যাগুলোই প্রদর্শন করুক।
যখন আপনার ওয়েব, মোবাইল, এবং একাউন্টিং এক্সপোর্টের মধ্যে সংখ্যা আলাদা হয়, সাধারণত সেটা গণিতের সমস্যা নয়—এটা স্টোরেজ ও রাউন্ডিং সমস্যা।
প্রতিটি ক্লায়েন্টকে যা ইনভয়েস স্টোর করে তা প্রদর্শন করানো নীতি থেকে শুরু করুন, পুনরায় গণনা না করালে মিল বজায় থাকে। আপনার ব্যাকএন্ডকে একক সোর্স-অফ-ট্রুথ বানান এবং প্রতিটি চ্যানেল একই সেভ করা মান পড়ুক।
রিফান্ড ও ক্রেডিট নোটগুলো মূল ইনভয়েসের রাউন্ডিং ফলাফলগুলো মিরর করা উচিত। যদি মূল ইনভয়েসে per-line ট্যাক্স রাউন্ডিং ছিল, রিফান্ডও একইভাবে করা উচিত এবং একই মুদ্রা প্রিসিশন ও স্টোরড FX রেট ব্যবহার করা উচিত। নইলে ছোট রিমেইন্ডারগুলো গড়ে উঠে সময়ের সাথে সমস্যা করবে।
ব্যবহারিক উপায়: প্রতিটি ইনভয়েসের সাথে একটি স্পষ্ট ক্যালকুলেশন স্ন্যাপশট সংরক্ষণ করুন: মুদ্রা, মাইনর ইউনিট প্রিসিশন, রাউন্ডিং মোড, FX রেট ও টাইমস্ট্যাম্প, এবং চূড়ান্ত লাইনের মাইনরগুলো।
এখানে একটি ইনভয়েস যা সব জায়গায় মিল থাকে বলে ধরা যাক।
ধরা যাক ইনভয়েস EUR-এ ইস্যু (2 দশমিক), VAT 20%, এবং গ্রাহক USD-এ চার্জ হয়েছে। ব্যাকএন্ড একটি FX স্ন্যাপশট স্টোর করে: 1 EUR = 1.0857 USD।
| Item | Net (EUR) |\n|---|---:|\n| Pro plan (monthly) | 19.99 |\n| Extra seats | 10.00 |\n| Discount (10% of 29.99, rounded) | -3.00 |
Net total (EUR) = 26.99
VAT 20% (EUR) = 5.40 (কারণ 26.99 x 0.20 = 5.398, রাউন্ড করে 5.40)
Gross total (EUR) = 32.39
এখন ব্যাকএন্ড স্টোর করা EUR টোটাল থেকে চার্জ কারেন্সি টোটাল উদঘাটন করে স্টোর করা FX স্ন্যাপশট ব্যবহার করে:
যদি আপনি USD প্রতি-লাইনে আলাদাভাবে স্টোর করেন, প্রত্যেকটি রূপান্তরিত লাইন রাউন্ড করে যোগ করলে প্রায়ই 0.01 পার্থক্য দেখা দেয়। এটাই ইনভয়েসগুলো সাধারণত ড্রিফটে নিয়ে যায়।
একে ডিটারমিনিস্টিক করুন: প্রতিটি লাইন রূপান্তর করে রাউন্ড করুন, তারপর যদি মোটগুলো মিলে না যায় তাহলে একটি নির্দিষ্ট অর্ডারে বাকি সেন্টগুলো (পজিটিভ বা নেগেটিভ) বিতরণ করুন (উদাহরণ: line_id আংশিক ক্রমে) যতক্ষণ না per-line যোগফল ফিক্সড গ্রস USD টোটালের সমান হয়।
ওয়েব ও মোবাইলকে ব্যাকএন্ড-স্টোর করা লাইন টোটাল, ট্যাক্স টোটাল, FX রেট, ও গ্রস দেখাতে বলুন, নিজে পুনরায় গণনা না করতে। একাউন্টিং এক্সপোর্ট একই স্টোর করা সংখ্যাগুলো ও FX স্ন্যাপশট (রেট, টাইমস্ট্যাম্প বা সোর্স) উত্পন্ন করবে যাতে লেজার গ্রাহক যা দেখেছে তার সঙ্গে মিলে।
প্রায়োগিক পরবর্তী ধাপ হলো গণনাটি একটি শেয়ার্ড সার্ভিস হিসেবে ইমপ্লিমেন্ট করা যা একটি একক ইনভয়েস স্ন্যাপশট আউটপুট করে (লাইন্স, ট্যাক্স, টোটাল, FX, রাউন্ডিং অ্যাডজাস্টমেন্ট) এবং সব চ্যানেল একই সেভ করা মান থেকে রেন্ডার করবে। যদি আপনি এই ফ্লোগুলো Koder.ai (koder.ai)-তে তৈরি করেন, এই স্ন্যাপশট মডেলটা সামনে রাখলে ওয়েব, মোবাইল ও এক্সপোর্টগুলো একরকমেই সিঙ্ক থাকবে কারণ সেসব একই সংরক্ষিত মান পড়তে পারবে।
কারণ প্রতিটি সিস্টেম প্রায়ই সামান্য ভিন্ন সিদ্ধান্ত নেয় কখন রাউন্ড করবে, কিসে রাউন্ড করবে (নেট বনাম গ্রস), এবং ট্যাক্স ও FX-এর জন্য কি প্রিসিশন রাখবে। ওই ক্ষুদ্র পার্থক্যগুলো প্রো-রেটিং, ক্রেডিট, এবং রিট্রাই চার্জের কারণে 0.01–0.02 ইউরোর গ্যাপে পরিণত হতে পারে।
অ্যাকাউন্টিং ও বিলিং-এ মানগুলো ফ্লোট হিসেবে সংরক্ষণ করবেন না। সংখ্যাগুলোকে মাইনর ইউনিটে পূর্ণসংখ্যা হিসেবে (যেমন সেন্ট) স্টোর করুন এবং সাথে মুদ্রার কোড রাখুন; কেবল ডিসপ্লের সময় ফরম্যাট করুন। ভাসমান-বিন্দু সংখ্যাগুলো অনেক দশমিক ঠিকমত প্রকাশ করতে পারে না, ফলে যোগ-করা বা ট্যাক্স প্রয়োগের সময় ছোট ছোট ত্রুটি জমে যায়।
একটিকে স্টোরড সোর্স-অফ-ট্রুথ হিসেবে বেছে নিন এবং অন্যগুলো সব জায়গায় একরকমভাবে উদঘাটন করুন। সাধারণ ডিফল্ট হলো নেট এবং ট্যাক্স মাইনর ইউনিটে স্টোর করা এবং গ্রস = নেট + ট্যাক্স হিসেবে গণনা করা, কারণ এতে রিফান্ড ও অডিট সহজ হয় এবং টোটাল স্থিতিশীল থাকে।
ইনভয়েস কারেন্সি হলো সেই মুদ্রা যেটিতে ইনভয়েসের লিগ্যাল টোটাল প্রকাশিত হয় এবং যার বিরুদ্ধে আপনাকে রিকনসাইল করতে হবে। ডিসপ্লে কারেন্সি হলো ইউআই-তে মূল্য দেখানোর জন্য ব্যবহৃত মুদ্রা, আর সেটলমেন্ট কারেন্সি হলো যেটিতে পেমেন্ট প্রসেসর ব্যাঙ্কে জমা করে। এগুলো আলাদা হতে পারে; যতক্ষণ ইনভয়েস কারেন্সির গণনাগুলো কনসিস্টেন্ট থাকে, ইনভয়েসে ত্রুটি নেই।
একটি ইনভয়েস একবার ইস্যু হয়ে গেলে এক্সপোর্টের সময় রেট পুনরায় না টেনে সেভ করা FX রেট (ভ্যালু, প্রিসিশন, প্রোভাইডার ও কার্যকর সময়) ব্যবহার করুন। রেট পুনরায় ফেচ করলে পুরনো ইনভয়েসগুলো ভিন্ন সংখ্যা দেখাবে।
একটি নিয়ম বেছে নিয়ে সব জায়গায় প্রয়োগ করুন: হয় “ইনভয়েস ইস্যু সময়ে রেট লক” অথবা “পেমেন্ট ক্যাপচার সময়ে লক” — মিশ্রতা করলে মিল থাকবে না, বিশেষ করে মিধন রাত্রি বা টাইমজোন বাউন্ডারির কাছাকাছি।
সাবস্ক্রিপশনের জন্য সাধারণত প্রতি লাইন রাউন্ডিং (round per line) নিরাপদ ডিফল্ট: এটি গ্রাহকদের বোঝাতে সহজ, অডিট করা সহজ এবং যদি সব চ্যানেল একই নিয়ম মেনে চলে তাহলে রিনিউয়ালগুলিতেও স্থিতিশীল থাকে।
স্পষ্টভাবে per-line ট্যাক্স রাউন্ডিং আর invoice-level ট্যাক্স রাউন্ডিং-এর মধ্যে একটা বেছে নিন, তারপর তা ডিটারমিনিস্টিক করুন। যদি আপনারকে ইনভয়েস-লেভেল লক্ষ্য পূরণ করতে হয়, তাহলে রাউন্ডিং রিমেইন্ডার নির্দিষ্ট নিয়মে বিতরণ করুন এবং প্রতিটি লাইনের চূড়ান্ত ট্যাক্স মাইনর হিসেবে সংরক্ষণ করুন যাতে সব সিস্টেম একই ফল দেখাতে পারে।
প্রো-রেটিং ভগ্নাংশ তৈরি করে (উদাহরণ: দৈনিক মূল্যে 9.3333...), তাই অপারেশনগুলোর অর্ডার গুরুত্বপূর্ণ। একটি পদ্ধতি বেছে নিন (উদাহরণ: প্রথমে নেট প্রো-রেট, তারপর স্টোর করা নেট থেকে ট্যাক্স হিসাব) এবং চূড়ান্ত লাইনের মাইনরগুলো স্টোর করুন যাতে আপগ্রেড, ডাউনগ্রেড, ক্রেডিট ও রিফান্ডগুলো মূল গণনার তুলনায় সমানভাবে ফিরে আসে।
সরলতম আর্কিটেকচার হলো ব্যাকএন্ড একটি চূড়ান্ত ইনভয়েস স্ন্যাপশট তৈরি করে (লাইন্স, ট্যাক্স, টোটালস, মুদ্রা মাইনর-ইউনিট নিয়ম, FX স্ন্যাপশট, রাউন্ডিং মোড) এবং এটি ফাইনাল হয়ে গেলে অপরিবর্তনীয় হিসেবে ধরে রাখা। ওয়েব, মোবাইল, পিডিএফ ও এক্সপোর্টগুলো ওই সেভ করা পূর্ণসংখ্যাগুলো রেন্ডার করুক, নিজে পুনরায় গণনা না করুক।