Cash matching
This is a cash matching problem. You can track this at one of two levels:
Compare invoiced to cash figures (somewhat sloppy but this is actually how it's done for inwards business by most Lloyd's Syndicates, often called a 'written vs. signed' report).
Maintain explicit cash allocations from cash payments broken down by invoice.
From your question I think you want to do the latter.
Typically this is done by having a separate set of cash transactions, and a bridging table that has the allocation of cash payments to invoices. If the values are equal or the cash payment comes with a single invoice reference you can do the allocation automatically. If there's a M:M relationship between invoices and payments you will need to do a manual matching process (doing this automatically is actually a variant of the knapsack problem).
A basic cash matching system
Imagine that you have an invoice table, a cash payments table and an allocation table. When you issue an invoice then you set up an invoice record in the invoices table and a 'receivable' or 'payable' record in the allocations table.
Now, you get a cash payment of $100
Cash payments (chq #12345): $100
Allocation: a record with a reference to invoice #1 and chq #12345, 'cash' transaction type and -100 owing ($100 paid).
You can generalise this to a M:M relationship where you get multiple payments against a single invoice or a payment covering multiple invoices. This structure also makes it quite easy to build credit control reports. The report just needs to find invoices older than (say) 180 days that still have outstanding balances.
Here's an example of the schema plus a couple of scenarios and an aged debt query. Unfortunately I don't have a running mysql instance to hand, so this one is for SQL Server.
-- ==============================================================
-- === CashMatch.sql ============================================
-- ==============================================================
--
-- === Invoices =================================================
--
create table Invoice (
InvoiceID int identity (1,1) not null
,InvoiceRef varchar (20)
,Amount money
,InvoiceDate datetime
)
go
alter table Invoice
add constraint PK_Invoice
primary key nonclustered (InvoiceID)
go
-- === Cash Payments ============================================
--
create table CashPayment (
CashPaymentID int identity (1,1) not null
,CashPaymentRef varchar (20)
,Amount money
,PaidDate datetime
)
go
alter table CashPayment
add constraint PK_CashPayment
primary key nonclustered (CashPaymentID)
go
-- === Allocations ==============================================
--
create table Allocation (
AllocationID int identity (1,1) not null
,CashPaymentID int -- Note that some records are not
,InvoiceID int -- on one side.
,AllocatedAmount money
,AllocationType varchar (20)
,TransactionDate datetime
)
go
alter table Allocation
add constraint PK_Allocation
primary key nonclustered (AllocationID)
go
-- ==============================================================
-- === Scenarios ================================================
-- ==============================================================
--
declare @Invoice1ID int
,@Invoice2ID int
,@PaymentID int
-- === Raise a new invoice ======================================
--
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('001', 100, '2012-01-01')
set @Invoice1ID = @@identity
insert Allocation (
InvoiceID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, 100, '2012-01-01', 'receivable')
-- === Receive a payment ========================================
--
insert CashPayment (CashPaymentRef, Amount, PaidDate)
values ('12345', 100, getdate())
set @PaymentID = @@identity
insert Allocation (
InvoiceID
,CashPaymentID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, @PaymentID, -100, getdate(), 'paid')
-- === Raise two invoices =======================================
--
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('002', 75, '2012-01-01')
set @Invoice1ID = @@identity
insert Allocation (
InvoiceID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, 75, '2012-01-01', 'receivable')
insert Invoice (InvoiceRef, Amount, InvoiceDate)
values ('003', 75, '2012-01-01')
set @Invoice2ID = @@identity
insert Allocation (
InvoiceID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice2ID, 75, '2012-01-01', 'receivable')
-- === Receive a payment ========================================
-- The payment covers one invoice in full and part of the other.
--
insert CashPayment (CashPaymentRef, Amount, PaidDate)
values ('23456', 120, getdate())
set @PaymentID = @@identity
insert Allocation (
InvoiceID
,CashPaymentID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice1ID, @PaymentID, -75, getdate(), 'paid')
insert Allocation (
InvoiceID
,CashPaymentID
,AllocatedAmount
,TransactionDate
,AllocationType
) values (@Invoice2ID, @PaymentID, -45, getdate(), 'paid')
-- === Aged debt report ========================================
--
select i.InvoiceRef
,sum (a.AllocatedAmount) as Owing
,datediff (dd, i.InvoiceDate, getdate()) as Age
from Invoice i
join Allocation a
on a.InvoiceID = i.InvoiceID
group by i.InvoiceRef
,datediff (dd, i.InvoiceDate, getdate())
having sum (a.AllocatedAmount) > 0
Best Answer
The fact that you are finding it difficult to explain indicates that there is an issue with the design, the expectations, or the perceived requirements.
Are
Subscriptions
automatically paid for, such as through a credit card? Is that why anInvoice
seems unnecessary? Or some other reason.Your current state is:
Customers
can purchase your products in one of two ways:Orders
requireInvoices
. Perhaps you need to bill them so as to be paid.Subscriptions
have noInvoices
. Perhaps subscriptions are automatically paid through a credit card, PayPal, etc.Since a
Customer
decides how to interact with you, thatCustomer
may have severalSubscriptions
and also may placeOrders
. And I would think that the customer could cancel theSubscription
whenever he wanted to.Therefore, both you and your customer have reason to keep a complete trail of orders and fulfillments (whether
Invoice
orSubscription
). That means that you do potentially want anInvoice
of some sort even forSubscriptions
.Here are two ways to handle this, namely treat them as the same thing or as two different things.
Subscriptions
create anInvoice
for the fulfillment of a subscription instance. The subscriptionInvoice
could have a different scheme (e.g. SUB-Number-Date) for identifying the subscriptionInvoice
than you would use for anInvoice
for anOrder
(e.g INV:InvoiceNumber). [I would guess that the subscriptionInvoice
would be marked as already paid.]Create a
Subscriptions
log with a header for the subscription and a log to track each fulfillment. That allows you to track the details of each timeCustomers
have theirSubscriptions
fulfilled. In the case that a subscription is cancelled early, it could be used to refund the customer the unused sum.How your organization views your
Customers
(person or businesses) will help you determine what is the best approach for your business.What would I do? I would tend would separate the
Order Invoice
from the more complexSubscription Invoice
. When needed a summary of the totality of Orders, Subscriptions, and (returns, reshipments, etc) can be produced for your company and for your customer.