! The invoice/payment matching problem.
You have sent a customer a number of invoices, say 100,
for 100 different services.  Your customer has sent you a number
of payments, say 20, without indicating which invoices
are covered by each payment. A payment typically covers several
invoices.  You want to match invoices
to payments, so as to identify which invoices are still not paid;
! Keywords: Accounting, Matching, Assignment, Finance, Subset sum, Invoicing;
SETS:
   invoice: iamt;
   payment: pamt, errup, errdn;
   ixp( invoice, payment): z;
ENDSETS
DATA: ! Invoice line item names, amounts; invoice, iamt = INV01 837.44 INV02 989.22 INV03 598.03 INV04 423.55 INV05 21.21 INV06 49.42 INV07 23.40 INV08 962.10 INV09 43.10 INV10 11.01 INV11 587.91 INV12 53.24 INV13 603.74 ; ! Payment names, amounts; payment, pamt = PAY01 1012.62 PAY02 1021.58 PAY03 880.54 PAY04 994.23 PAY05 1191.65; ENDDATA SUBMODEL matchinv: ! Variables: z(i,p) = 1 is invoice i is assigned to payment p; ! Each invoice can be assigned to at most 1 payment; @FOR( invoice(i): @SUM( ixp( i,p): z(i,p)) <= 1; ); ! You either assign it or not; @FOR( ixp( i,p): @BIN( z(i,p))); ! Compute error in matching payment p; @FOR( payment( p): errup( p) - errdn( p) = @SUM( ixp( i, p): iamt(i)*z(i,p)) - pamt(p); ); ! Minimize the relative error of matching each payment to 1 or more invoices; min = @SUM( payment( p): (errup(p) + errdn(p))/ pamt( p)); ! Minimize the absolute error; ! min = @sum( payment( p): errup(p) + errdn(p)); ENDSUBMODEL
CALC: @SOLVE( matchinv); @WRITE(' Matching payment amounts to invoice/line item amounts', @NEWLINE(1)); @FOR( payment( p): paycum = 0; @FOR( ixp( i, p ) | z(i,p) #gt# 0.5: @WRITE( @FORMAT( payment(p),'12s'),' ', @FORMAT( invoice(i),'12s'),' ',@FORMAT(iamt(i),'12.2f'),@NEWLINE(1)); paycum = paycum + iamt(i); ); @IFC( @ABS( pamt(p) - paycum) #lt# 0.01: @WRITE(' Total ', @FORMAT( pamt(p),'11.2f'),' vs. ',@FORMAT( paycum, '11.2f'), @NEWLINE(2)); @else @WRITE(' Total ', @FORMAT( pamt(p),'11.2f'),' *vs.* ',@FORMAT( paycum, '11.2f'), @NEWLINE(2)); ); ); paycum = 0; @FOR( invoice(i) | @SUM( ixp(i,p): z(i,p)) #lt# 0.5: @WRITE( ' Unassigned',@FORMAT( invoice(i),'12s'),' ', @FORMAT(iamt(i),'12.2f'), @NEWLINE(1)); paycum = paycum + iamt(i); ); @WRITE(' Total ',@FORMAT( paycum, '11.2f'), @NEWLINE(1)); ENDCALC