LP1174498 Payment by billing type breakdown
authorblake <blake@mobiusconsortium.org>
Fri, 25 Mar 2016 21:42:15 +0000 (16:42 -0500)
committerblake <blake@mobiusconsortium.org>
Wed, 27 Feb 2019 22:53:56 +0000 (16:53 -0600)
Corrected a situation where account_adjustment payments were not paying
the intended billing line items. The new function algorithm attempts to find
a billing line item for the exact amount of the payment in the case of
account_adjustment payment type.

Signed-off-by: blake <blake@mobiusconsortium.org>
Open-ILS/src/sql/Pg/080.schema.money.sql
Open-ILS/src/sql/Pg/upgrade/XXXX.money.materialized_payment_by_billing_type

index 79c8ed5..3686841 100644 (file)
@@ -689,20 +689,22 @@ CREATE OR REPLACE VIEW money.cashdrawer_payment_view AS
                LEFT JOIN money.payment_view t ON (p.id = t.id);
 
 CREATE TABLE money.materialized_payment_by_billing_type (
-    id              BIGSERIAL       PRIMARY KEY,
-    xact            BIGINT          NOT NULL,
-    payment         BIGINT          NOT NULL,
-    billing         BIGINT          NOT NULL,
-    payment_ts      TIMESTAMPTZ     NOT NULL,
-    billing_ts      TIMESTAMPTZ     NOT NULL,
-    amount          NUMERIC(8,2)    NOT NULL,
-    payment_type    TEXT,
-    billing_type    TEXT,
-    payment_ou      INT,
-    billing_ou      INT,
+       id              BIGSERIAL       PRIMARY KEY,
+       xact            BIGINT          NOT NULL,
+       payment         BIGINT          NOT NULL,
+       billing         BIGINT          NOT NULL,
+       payment_ts      TIMESTAMPTZ     NOT NULL,
+       billing_ts      TIMESTAMPTZ     NOT NULL,
+       amount          NUMERIC(8,2)    NOT NULL,
+       payment_type    TEXT,
+       billing_type    TEXT,
+       payment_ou      INT,
+       billing_ou      INT,
 -- Debug       billing_leftover NUMERIC(8,2),
 -- Debug       payment_leftover NUMERIC(8,2),
-    CONSTRAINT x_p_b_once UNIQUE (xact,payment,billing)
+-- Debug       used_billings    BIGINT[],
+-- Debug       billing_l               INT,
+       CONSTRAINT x_p_b_once UNIQUE (xact,payment,billing)
 );
 
 CREATE INDEX p_by_b_payment_ts_idx
@@ -715,19 +717,25 @@ CREATE OR REPLACE FUNCTION money.payment_by_billing_type (
 ) RETURNS SETOF money.materialized_payment_by_billing_type AS $$
 
 DECLARE
-    current_result      money.materialized_payment_by_billing_type%ROWTYPE;
-    current_payment     money.payment_view%ROWTYPE;
-    current_billing     money.billing%ROWTYPE;
-    payment_remainder   NUMERIC(8,2) := 0.0;
-    billing_remainder   NUMERIC(8,2) := 0.0;
-    payment_offset      INT := 0;
-    billing_offset      INT := 0;
-    billing_ou          INT := 0;
-    payment_ou          INT := 0;      
+       current_result      money.materialized_payment_by_billing_type%ROWTYPE;
+       current_payment     money.payment_view%ROWTYPE;
+       current_billing     money.billing%ROWTYPE;
+       payment_remainder   NUMERIC(8,2) := 0.0;
+       billing_remainder   NUMERIC(8,2) := 0.0;
+       payment_offset      INT := 0;
+       billing_offset      INT := 0;
+       billing_ou          INT := 0;
+       payment_ou          INT := 0;   
        fast_forward            BOOLEAN := FALSE;
        maintain_billing_remainder      BOOLEAN := FALSE;
        billing_loop            INT := -1;
        billing_row_count       INT := 0;
+       current_billing_id      BIGINT := 0;
+       billing_id_used         BIGINT ARRAY;
+       billing_l               INT := 0;
+       continuing_payment      BOOLEAN := FALSE;
+       continuing_payment_last_row     BOOLEAN := FALSE;
+       
 BEGIN
 
     /*  This algorithm is fairly straight-forward.  We take a transaction id
@@ -767,7 +775,7 @@ BEGIN
           ORDER BY payment_ts
     LOOP
 
-        payment_remainder = current_payment.amount;
+       payment_remainder = current_payment.amount;
                -- With every new payment row, we need to fast forward 
                -- the billing lines up to the last paid billing row            
                fast_forward := TRUE;
@@ -780,73 +788,87 @@ BEGIN
             payment_ou = billing_ou;
         END IF;
 
-        -- Were we looking at a billing from a previous step in the loop?
-        IF billing_remainder > 0.0 THEN                
-            current_result.xact = p_xact;
-            current_result.payment = current_payment.id;
-            current_result.billing = current_billing.id;
-            current_result.payment_ts = current_payment.payment_ts;
-            current_result.billing_ts = current_billing.billing_ts;
-            current_result.payment_type = current_payment.payment_type;
-            current_result.billing_type = current_billing.billing_type;
-            current_result.payment_ou = payment_ou;
-            current_result.billing_ou = billing_ou;
+               -- Were we looking at a billing from a previous step in the loop?
+               IF billing_remainder > 0.0 THEN         
+                       current_result.xact = p_xact;
+                       current_result.payment = current_payment.id;
+                       current_result.billing = current_billing.id;
+                       current_result.payment_ts = current_payment.payment_ts;
+                       current_result.billing_ts = current_billing.billing_ts;
+                       current_result.payment_type = current_payment.payment_type;
+                       current_result.billing_type = current_billing.billing_type;
+                       current_result.payment_ou = payment_ou;
+                       current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.used_billings = billing_id_used;
+-- Debug                       current_result.billing_l = billing_loop;
 
-            IF billing_remainder >= payment_remainder THEN
-                current_result.amount = payment_remainder;
-                billing_remainder = billing_remainder - payment_remainder;
-                payment_remainder = 0.0;
-                payment_offset = payment_offset + 1;
+                       IF billing_remainder >= payment_remainder THEN
+                               current_result.amount = payment_remainder;
+                               billing_remainder = billing_remainder - payment_remainder;
+                               payment_remainder = 0.0;
+                               payment_offset = payment_offset + 1;
                                -- If it is equal then we need to close up the billing line and move to the next
                                -- This prevents 0 amounts applied to billing lines
                                IF billing_remainder = payment_remainder THEN
                                        billing_remainder = 0.0;
-                                       billing_offset = billing_offset + 1;                                    
+                                       billing_offset = billing_offset + 1;
+                                       billing_id_used = array_append( billing_id_used, current_billing_id );
                                ELSE
                                        maintain_billing_remainder := TRUE;
                                END IF;
                                
-            ELSE
-                current_result.amount = billing_remainder;
-                payment_remainder = payment_remainder - billing_remainder;
-                billing_remainder = 0.0;
+                       ELSE
+                               current_result.amount = billing_remainder;
+                               payment_remainder = payment_remainder - billing_remainder;
+                               billing_remainder = 0.0;
                                billing_offset = billing_offset + 1;
-            END IF;
+                               billing_id_used = array_append( billing_id_used, current_billing_id );
+                               continuing_payment := TRUE;
+                       END IF;
 
-            RETURN NEXT current_result;
+                       RETURN NEXT current_result;
                        -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-                       EXIT WHEN billing_offset = billing_row_count+1;
-        END IF;
-
-        CONTINUE WHEN payment_remainder = 0.0;
+                       EXIT WHEN array_length(billing_id_used, 1) = billing_row_count;
+               END IF;
 
+               CONTINUE WHEN payment_remainder = 0.0;
+               
         -- next billing, please
-
+               billing_loop := -1;
+               
         FOR current_billing IN
             SELECT  * 
               FROM  money.billing
               WHERE xact = p_xact
-               --    AND NOT voided
                           -- Gotta put the voided billing rows at the bottom (last)
               ORDER BY voided,billing_ts
         LOOP
-                       -- Fast forward through the rows until we get to the billing row 
-                       -- where we left off
-                       IF fast_forward THEN                                                            
-                               billing_loop = billing_loop + 1; -- first iteration billing_loop=0, it starts at -1
-                               CONTINUE WHEN billing_loop <> billing_offset;
+                       billing_loop = billing_loop + 1;
+                       
+                       -- Skip billing rows that we have already paid
+                       IF billing_id_used @> ARRAY[current_billing.id] THEN CONTINUE;
+                       END IF;
+                       
+                       IF maintain_billing_remainder THEN
+                               CONTINUE WHEN current_billing.id <> current_billing_id;
+                               -- Account adjustment - we expect to pay billing rows that are identical amounts
+                               ELSE IF current_payment.payment_type = 'account_adjustment' THEN
+                                       -- Go ahead and allow the row through when it's the last row and we still haven't found one with equal payment amount
+                                       CONTINUE WHEN ( ( current_billing.amount <> current_payment.amount ) AND ( billing_loop + 1 <> billing_row_count ) );
+                               END IF;
                        END IF;
                        
                        -- Keep the old remainder if we were in the middle of a billing row
                        IF NOT maintain_billing_remainder THEN  
-                               billing_remainder = current_billing.amount;                     
+                               billing_remainder = current_billing.amount;
                        END IF;
                        
-                       maintain_billing_remainder := FALSE;
-                       billing_loop := -1;
+                       maintain_billing_remainder := FALSE;                    
                        fast_forward := FALSE;
+                       current_billing_id := current_billing.id;
+                       continuing_payment := FALSE;
                        
             current_result.xact = p_xact;
             current_result.payment = current_payment.id;
@@ -859,36 +881,98 @@ BEGIN
             current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.used_billings = billing_id_used;
+-- Debug                       current_result.billing_l = billing_loop;
 
             IF billing_remainder >= payment_remainder THEN
                 current_result.amount = payment_remainder;
                 billing_remainder = billing_remainder - payment_remainder;
-                IF NOT current_billing.voided THEN
-                    payment_remainder = 0.0;
-                END IF;
+                               payment_remainder = 0.0;
                                -- If it is equal then we need to close up the billing line and move to the next
                                -- This prevents 0 amounts applied to billing lines
                                IF billing_remainder = payment_remainder THEN
                                        billing_remainder = 0.0;
-                                       billing_offset = billing_offset + 1;                    
+                                       billing_offset = billing_offset + 1;
+                                       billing_id_used = array_append( billing_id_used, current_billing_id );                                  
                                END IF;
             ELSE
                 current_result.amount = billing_remainder;
-                IF NOT current_billing.voided THEN
-                    payment_remainder = payment_remainder - billing_remainder;
-                END IF;
+                               payment_remainder = payment_remainder - billing_remainder;
+                               continuing_payment := TRUE;
+                               IF billing_loop + 1 = billing_row_count THEN
+                               -- We have a situation where we are on the last billing row and we are in the middle of a payment row
+                               -- We need to start back at the beginning of the billing rows and pay
+                                       continuing_payment_last_row := TRUE;
+                               END IF;
                 billing_remainder = 0.0;
                 billing_offset = billing_offset + 1;
+                               billing_id_used = array_append( billing_id_used, current_billing_id );
             END IF;
        
             RETURN NEXT current_result;
-
+                       IF continuing_payment_last_row THEN
+                               -- This should only occur when the account_adjustment's do not line up exactly with the billing
+                               -- So we are going to pay some other type of billing row with this odd account_adjustment
+                               -- And we need to stay in the current_payment row while doing so
+                               billing_loop := -1;
+                               FOR current_billing IN
+                                       SELECT  * 
+                                         FROM  money.billing
+                                         WHERE xact = p_xact
+                                         ORDER BY voided,billing_ts
+                               LOOP
+                                       billing_loop = billing_loop + 1;
+                                       -- Skip billing rows that we have already paid
+                                       IF billing_id_used @> ARRAY[current_billing.id] THEN CONTINUE; END IF;
+                                       
+                                       billing_remainder = current_billing.amount;
+                                       current_billing_id := current_billing.id;
+                                       continuing_payment := FALSE;
+                                       
+                                       current_result.xact = p_xact;
+                                       current_result.payment = current_payment.id;
+                                       current_result.billing = current_billing.id;
+                                       current_result.payment_ts = current_payment.payment_ts;
+                                       current_result.billing_ts = current_billing.billing_ts;
+                                       current_result.payment_type = current_payment.payment_type;
+                                       current_result.billing_type = current_billing.billing_type;
+                                       current_result.payment_ou = payment_ou;
+                                       current_result.billing_ou = billing_ou;
+-- Debug                                       current_result.billing_leftover = billing_remainder;
+-- Debug                                       current_result.payment_leftover = payment_remainder;
+-- Debug                                       current_result.used_billings = billing_id_used;
+-- Debug                                       current_result.billing_l = billing_loop;
+
+                                       IF billing_remainder >= payment_remainder THEN
+                                               current_result.amount = payment_remainder;
+                                               billing_remainder = billing_remainder - payment_remainder;
+                                               payment_remainder = 0.0;
+                                               -- If it is equal then we need to close up the billing line and move to the next
+                                               -- This prevents 0 amounts applied to billing lines
+                                               IF billing_remainder = payment_remainder THEN
+                                                       billing_remainder = 0.0;
+                                                       billing_offset = billing_offset + 1;
+                                                       billing_id_used = array_append( billing_id_used, current_billing_id );                                  
+                                               END IF;
+                                       ELSE
+                                               current_result.amount = billing_remainder;
+                                               payment_remainder = payment_remainder - billing_remainder;
+                                               billing_remainder = 0.0;
+                                               billing_offset = billing_offset + 1;
+                                               billing_id_used = array_append( billing_id_used, current_billing_id );
+                                       END IF;
+                          
+                                       RETURN NEXT current_result;
+                                       EXIT WHEN payment_remainder = 0.0;
+                               END LOOP;
+                               
+                       END IF;
             EXIT WHEN payment_remainder = 0.0;
         END LOOP;
 
         payment_offset = payment_offset + 1;
                -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-               EXIT WHEN billing_offset = billing_row_count+1;
+               EXIT WHEN array_length(billing_id_used, 1) = billing_row_count;
 
     END LOOP;
 
@@ -948,6 +1032,7 @@ BEGIN
             current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.billing_l = billing_loop;
 
             IF billing_remainder >= payment_remainder THEN
                                current_result.amount = payment_remainder;
@@ -972,13 +1057,13 @@ BEGIN
             current_result.amount = -current_result.amount;
             RETURN NEXT current_result;
                        -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-                       EXIT WHEN billing_offset = billing_row_count+1;
+                       EXIT WHEN billing_offset = billing_row_count + 1;
         END IF;
 
         CONTINUE WHEN payment_remainder = 0.0;
 
         -- next billing, please
-
+               billing_loop := -1;
         FOR current_billing IN 
             SELECT  *
               FROM  money.billing
@@ -986,10 +1071,10 @@ BEGIN
                     AND voided
               ORDER BY billing_ts
         LOOP
+                       billing_loop = billing_loop + 1; -- first iteration billing_loop=0, it starts at -1
                        -- Fast forward through the rows until we get to the billing row 
                        -- where we left off
-                       IF fast_forward THEN                                                            
-                               billing_loop = billing_loop + 1; -- first iteration billing_loop=0, it starts at -1
+                       IF fast_forward THEN
                                CONTINUE WHEN billing_loop <> billing_offset;
                        END IF;
                        
@@ -999,7 +1084,6 @@ BEGIN
                        END IF;
                        
                        maintain_billing_remainder := FALSE;
-                       billing_loop := -1;
                        fast_forward := FALSE;            
 
             current_result.xact = p_xact;
@@ -1013,6 +1097,7 @@ BEGIN
             current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.billing_l = billing_loop;
 
             IF billing_remainder >= payment_remainder THEN
                 current_result.amount = payment_remainder;
@@ -1040,7 +1125,7 @@ BEGIN
 
         payment_offset = payment_offset + 1;
                -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-               EXIT WHEN billing_offset = billing_row_count+1;
+               EXIT WHEN billing_offset = billing_row_count + 1;
 
     END LOOP;
 
@@ -1061,7 +1146,7 @@ DECLARE
     current_result      money.materialized_payment_by_billing_type%ROWTYPE;
 BEGIN
 
-    -- first, we find transactions at specified locations involivng
+    -- first, we find transactions at specified locations involving
     -- positve, unvoided payments within the specified range
     FOR current_transaction IN
         SELECT  DISTINCT x.id
index 1aedfc7..9b1c0f1 100644 (file)
@@ -4,20 +4,22 @@ SELECT evergreen.upgrade_deps_block_check('XXXX', :eg_version);
 
 
 CREATE TABLE money.materialized_payment_by_billing_type (
-    id              BIGSERIAL       PRIMARY KEY,
-    xact            BIGINT          NOT NULL,
-    payment         BIGINT          NOT NULL,
-    billing         BIGINT          NOT NULL,
-    payment_ts      TIMESTAMPTZ     NOT NULL,
-    billing_ts      TIMESTAMPTZ     NOT NULL,
-    amount          NUMERIC(8,2)    NOT NULL,
-    payment_type    TEXT,
-    billing_type    TEXT,
-    payment_ou      INT,
-    billing_ou      INT,
+       id              BIGSERIAL       PRIMARY KEY,
+       xact            BIGINT          NOT NULL,
+       payment         BIGINT          NOT NULL,
+       billing         BIGINT          NOT NULL,
+       payment_ts      TIMESTAMPTZ     NOT NULL,
+       billing_ts      TIMESTAMPTZ     NOT NULL,
+       amount          NUMERIC(8,2)    NOT NULL,
+       payment_type    TEXT,
+       billing_type    TEXT,
+       payment_ou      INT,
+       billing_ou      INT,
 -- Debug       billing_leftover NUMERIC(8,2),
 -- Debug       payment_leftover NUMERIC(8,2),
-    CONSTRAINT x_p_b_once UNIQUE (xact,payment,billing)
+-- Debug       used_billings    BIGINT[],
+-- Debug       billing_l               INT,
+       CONSTRAINT x_p_b_once UNIQUE (xact,payment,billing)
 );
 
 CREATE INDEX p_by_b_payment_ts_idx
@@ -30,19 +32,25 @@ CREATE OR REPLACE FUNCTION money.payment_by_billing_type (
 ) RETURNS SETOF money.materialized_payment_by_billing_type AS $$
 
 DECLARE
-    current_result      money.materialized_payment_by_billing_type%ROWTYPE;
-    current_payment     money.payment_view%ROWTYPE;
-    current_billing     money.billing%ROWTYPE;
-    payment_remainder   NUMERIC(8,2) := 0.0;
-    billing_remainder   NUMERIC(8,2) := 0.0;
-    payment_offset      INT := 0;
-    billing_offset      INT := 0;
-    billing_ou          INT := 0;
-    payment_ou          INT := 0;      
+       current_result      money.materialized_payment_by_billing_type%ROWTYPE;
+       current_payment     money.payment_view%ROWTYPE;
+       current_billing     money.billing%ROWTYPE;
+       payment_remainder   NUMERIC(8,2) := 0.0;
+       billing_remainder   NUMERIC(8,2) := 0.0;
+       payment_offset      INT := 0;
+       billing_offset      INT := 0;
+       billing_ou          INT := 0;
+       payment_ou          INT := 0;   
        fast_forward            BOOLEAN := FALSE;
        maintain_billing_remainder      BOOLEAN := FALSE;
        billing_loop            INT := -1;
        billing_row_count       INT := 0;
+       current_billing_id      BIGINT := 0;
+       billing_id_used         BIGINT ARRAY;
+       billing_l               INT := 0;
+       continuing_payment      BOOLEAN := FALSE;
+       continuing_payment_last_row     BOOLEAN := FALSE;
+       
 BEGIN
 
     /*  This algorithm is fairly straight-forward.  We take a transaction id
@@ -82,7 +90,7 @@ BEGIN
           ORDER BY payment_ts
     LOOP
 
-        payment_remainder = current_payment.amount;
+       payment_remainder = current_payment.amount;
                -- With every new payment row, we need to fast forward 
                -- the billing lines up to the last paid billing row            
                fast_forward := TRUE;
@@ -95,73 +103,87 @@ BEGIN
             payment_ou = billing_ou;
         END IF;
 
-        -- Were we looking at a billing from a previous step in the loop?
-        IF billing_remainder > 0.0 THEN                
-            current_result.xact = p_xact;
-            current_result.payment = current_payment.id;
-            current_result.billing = current_billing.id;
-            current_result.payment_ts = current_payment.payment_ts;
-            current_result.billing_ts = current_billing.billing_ts;
-            current_result.payment_type = current_payment.payment_type;
-            current_result.billing_type = current_billing.billing_type;
-            current_result.payment_ou = payment_ou;
-            current_result.billing_ou = billing_ou;
+               -- Were we looking at a billing from a previous step in the loop?
+               IF billing_remainder > 0.0 THEN         
+                       current_result.xact = p_xact;
+                       current_result.payment = current_payment.id;
+                       current_result.billing = current_billing.id;
+                       current_result.payment_ts = current_payment.payment_ts;
+                       current_result.billing_ts = current_billing.billing_ts;
+                       current_result.payment_type = current_payment.payment_type;
+                       current_result.billing_type = current_billing.billing_type;
+                       current_result.payment_ou = payment_ou;
+                       current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.used_billings = billing_id_used;
+-- Debug                       current_result.billing_l = billing_loop;
 
-            IF billing_remainder >= payment_remainder THEN
-                current_result.amount = payment_remainder;
-                billing_remainder = billing_remainder - payment_remainder;
-                payment_remainder = 0.0;
-                payment_offset = payment_offset + 1;
+                       IF billing_remainder >= payment_remainder THEN
+                               current_result.amount = payment_remainder;
+                               billing_remainder = billing_remainder - payment_remainder;
+                               payment_remainder = 0.0;
+                               payment_offset = payment_offset + 1;
                                -- If it is equal then we need to close up the billing line and move to the next
                                -- This prevents 0 amounts applied to billing lines
                                IF billing_remainder = payment_remainder THEN
                                        billing_remainder = 0.0;
-                                       billing_offset = billing_offset + 1;                                    
+                                       billing_offset = billing_offset + 1;
+                                       billing_id_used = array_append( billing_id_used, current_billing_id );
                                ELSE
                                        maintain_billing_remainder := TRUE;
                                END IF;
                                
-            ELSE
-                current_result.amount = billing_remainder;
-                payment_remainder = payment_remainder - billing_remainder;
-                billing_remainder = 0.0;
+                       ELSE
+                               current_result.amount = billing_remainder;
+                               payment_remainder = payment_remainder - billing_remainder;
+                               billing_remainder = 0.0;
                                billing_offset = billing_offset + 1;
-            END IF;
+                               billing_id_used = array_append( billing_id_used, current_billing_id );
+                               continuing_payment := TRUE;
+                       END IF;
 
-            RETURN NEXT current_result;
+                       RETURN NEXT current_result;
                        -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-                       EXIT WHEN billing_offset = billing_row_count+1;
-        END IF;
-
-        CONTINUE WHEN payment_remainder = 0.0;
+                       EXIT WHEN array_length(billing_id_used, 1) = billing_row_count;
+               END IF;
 
+               CONTINUE WHEN payment_remainder = 0.0;
+               
         -- next billing, please
-
+               billing_loop := -1;
+               
         FOR current_billing IN
             SELECT  * 
               FROM  money.billing
               WHERE xact = p_xact
-               --    AND NOT voided
                           -- Gotta put the voided billing rows at the bottom (last)
               ORDER BY voided,billing_ts
         LOOP
-                       -- Fast forward through the rows until we get to the billing row 
-                       -- where we left off
-                       IF fast_forward THEN                                                            
-                               billing_loop = billing_loop + 1; -- first iteration billing_loop=0, it starts at -1
-                               CONTINUE WHEN billing_loop <> billing_offset;
+                       billing_loop = billing_loop + 1;
+                       
+                       -- Skip billing rows that we have already paid
+                       IF billing_id_used @> ARRAY[current_billing.id] THEN CONTINUE;
+                       END IF;
+                       
+                       IF maintain_billing_remainder THEN
+                               CONTINUE WHEN current_billing.id <> current_billing_id;
+                               -- Account adjustment - we expect to pay billing rows that are identical amounts
+                               ELSE IF current_payment.payment_type = 'account_adjustment' THEN
+                                       -- Go ahead and allow the row through when it's the last row and we still haven't found one with equal payment amount
+                                       CONTINUE WHEN ( ( current_billing.amount <> current_payment.amount ) AND ( billing_loop + 1 <> billing_row_count ) );
+                               END IF;
                        END IF;
                        
                        -- Keep the old remainder if we were in the middle of a billing row
                        IF NOT maintain_billing_remainder THEN  
-                               billing_remainder = current_billing.amount;                     
+                               billing_remainder = current_billing.amount;
                        END IF;
                        
-                       maintain_billing_remainder := FALSE;
-                       billing_loop := -1;
+                       maintain_billing_remainder := FALSE;                    
                        fast_forward := FALSE;
+                       current_billing_id := current_billing.id;
+                       continuing_payment := FALSE;
                        
             current_result.xact = p_xact;
             current_result.payment = current_payment.id;
@@ -174,36 +196,98 @@ BEGIN
             current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.used_billings = billing_id_used;
+-- Debug                       current_result.billing_l = billing_loop;
 
             IF billing_remainder >= payment_remainder THEN
                 current_result.amount = payment_remainder;
                 billing_remainder = billing_remainder - payment_remainder;
-                IF NOT current_billing.voided THEN
-                    payment_remainder = 0.0;
-                END IF;
+                               payment_remainder = 0.0;
                                -- If it is equal then we need to close up the billing line and move to the next
                                -- This prevents 0 amounts applied to billing lines
                                IF billing_remainder = payment_remainder THEN
                                        billing_remainder = 0.0;
-                                       billing_offset = billing_offset + 1;                    
+                                       billing_offset = billing_offset + 1;
+                                       billing_id_used = array_append( billing_id_used, current_billing_id );                                  
                                END IF;
             ELSE
                 current_result.amount = billing_remainder;
-                IF NOT current_billing.voided THEN
-                    payment_remainder = payment_remainder - billing_remainder;
-                END IF;
+                               payment_remainder = payment_remainder - billing_remainder;
+                               continuing_payment := TRUE;
+                               IF billing_loop + 1 = billing_row_count THEN
+                               -- We have a situation where we are on the last billing row and we are in the middle of a payment row
+                               -- We need to start back at the beginning of the billing rows and pay
+                                       continuing_payment_last_row := TRUE;
+                               END IF;
                 billing_remainder = 0.0;
                 billing_offset = billing_offset + 1;
+                               billing_id_used = array_append( billing_id_used, current_billing_id );
             END IF;
        
             RETURN NEXT current_result;
-
+                       IF continuing_payment_last_row THEN
+                               -- This should only occur when the account_adjustment's do not line up exactly with the billing
+                               -- So we are going to pay some other type of billing row with this odd account_adjustment
+                               -- And we need to stay in the current_payment row while doing so
+                               billing_loop := -1;
+                               FOR current_billing IN
+                                       SELECT  * 
+                                         FROM  money.billing
+                                         WHERE xact = p_xact
+                                         ORDER BY voided,billing_ts
+                               LOOP
+                                       billing_loop = billing_loop + 1;
+                                       -- Skip billing rows that we have already paid
+                                       IF billing_id_used @> ARRAY[current_billing.id] THEN CONTINUE; END IF;
+                                       
+                                       billing_remainder = current_billing.amount;
+                                       current_billing_id := current_billing.id;
+                                       continuing_payment := FALSE;
+                                       
+                                       current_result.xact = p_xact;
+                                       current_result.payment = current_payment.id;
+                                       current_result.billing = current_billing.id;
+                                       current_result.payment_ts = current_payment.payment_ts;
+                                       current_result.billing_ts = current_billing.billing_ts;
+                                       current_result.payment_type = current_payment.payment_type;
+                                       current_result.billing_type = current_billing.billing_type;
+                                       current_result.payment_ou = payment_ou;
+                                       current_result.billing_ou = billing_ou;
+-- Debug                                       current_result.billing_leftover = billing_remainder;
+-- Debug                                       current_result.payment_leftover = payment_remainder;
+-- Debug                                       current_result.used_billings = billing_id_used;
+-- Debug                                       current_result.billing_l = billing_loop;
+
+                                       IF billing_remainder >= payment_remainder THEN
+                                               current_result.amount = payment_remainder;
+                                               billing_remainder = billing_remainder - payment_remainder;
+                                               payment_remainder = 0.0;
+                                               -- If it is equal then we need to close up the billing line and move to the next
+                                               -- This prevents 0 amounts applied to billing lines
+                                               IF billing_remainder = payment_remainder THEN
+                                                       billing_remainder = 0.0;
+                                                       billing_offset = billing_offset + 1;
+                                                       billing_id_used = array_append( billing_id_used, current_billing_id );                                  
+                                               END IF;
+                                       ELSE
+                                               current_result.amount = billing_remainder;
+                                               payment_remainder = payment_remainder - billing_remainder;
+                                               billing_remainder = 0.0;
+                                               billing_offset = billing_offset + 1;
+                                               billing_id_used = array_append( billing_id_used, current_billing_id );
+                                       END IF;
+                          
+                                       RETURN NEXT current_result;
+                                       EXIT WHEN payment_remainder = 0.0;
+                               END LOOP;
+                               
+                       END IF;
             EXIT WHEN payment_remainder = 0.0;
         END LOOP;
 
         payment_offset = payment_offset + 1;
                -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-               EXIT WHEN billing_offset = billing_row_count+1;
+               EXIT WHEN array_length(billing_id_used, 1) = billing_row_count;
 
     END LOOP;
 
@@ -263,6 +347,7 @@ BEGIN
             current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.billing_l = billing_loop;
 
             IF billing_remainder >= payment_remainder THEN
                                current_result.amount = payment_remainder;
@@ -287,13 +372,13 @@ BEGIN
             current_result.amount = -current_result.amount;
             RETURN NEXT current_result;
                        -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-                       EXIT WHEN billing_offset = billing_row_count+1;
+                       EXIT WHEN billing_offset = billing_row_count + 1;
         END IF;
 
         CONTINUE WHEN payment_remainder = 0.0;
 
         -- next billing, please
-
+               billing_loop := -1;
         FOR current_billing IN 
             SELECT  *
               FROM  money.billing
@@ -301,10 +386,10 @@ BEGIN
                     AND voided
               ORDER BY billing_ts
         LOOP
+                       billing_loop = billing_loop + 1; -- first iteration billing_loop=0, it starts at -1
                        -- Fast forward through the rows until we get to the billing row 
                        -- where we left off
-                       IF fast_forward THEN                                                            
-                               billing_loop = billing_loop + 1; -- first iteration billing_loop=0, it starts at -1
+                       IF fast_forward THEN
                                CONTINUE WHEN billing_loop <> billing_offset;
                        END IF;
                        
@@ -314,7 +399,6 @@ BEGIN
                        END IF;
                        
                        maintain_billing_remainder := FALSE;
-                       billing_loop := -1;
                        fast_forward := FALSE;            
 
             current_result.xact = p_xact;
@@ -328,6 +412,7 @@ BEGIN
             current_result.billing_ou = billing_ou;
 -- Debug                       current_result.billing_leftover = billing_remainder;
 -- Debug                       current_result.payment_leftover = payment_remainder;
+-- Debug                       current_result.billing_l = billing_loop;
 
             IF billing_remainder >= payment_remainder THEN
                 current_result.amount = payment_remainder;
@@ -355,7 +440,7 @@ BEGIN
 
         payment_offset = payment_offset + 1;
                -- Done paying the billing rows when we run out of rows to pay (out of bounds)
-               EXIT WHEN billing_offset = billing_row_count+1;
+               EXIT WHEN billing_offset = billing_row_count + 1;
 
     END LOOP;
 
@@ -376,7 +461,7 @@ DECLARE
     current_result      money.materialized_payment_by_billing_type%ROWTYPE;
 BEGIN
 
-    -- first, we find transactions at specified locations involivng
+    -- first, we find transactions at specified locations involving
     -- positve, unvoided payments within the specified range
     FOR current_transaction IN
         SELECT  DISTINCT x.id