org.openvpms.archetype.rules.stock.ChargeStockUpdaterTestCase.java Source code

Java tutorial

Introduction

Here is the source code for org.openvpms.archetype.rules.stock.ChargeStockUpdaterTestCase.java

Source

/*
 * Version: 1.0
 *
 * The contents of this file are subject to the OpenVPMS License Version
 * 1.0 (the 'License'); you may not use this file except in compliance with
 * the License. You may obtain a copy of the License at
 * http://www.openvpms.org/license/
 *
 * Software distributed under the License is distributed on an 'AS IS' basis,
 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
 * for the specific language governing rights and limitations under the
 * License.
 *
 * Copyright 2014 (C) OpenVPMS Ltd. All Rights Reserved.
 */

package org.openvpms.archetype.rules.stock;

import org.junit.Before;
import org.junit.Test;
import org.openvpms.archetype.rules.act.ActStatus;
import org.openvpms.archetype.rules.finance.account.CustomerAccountArchetypes;
import org.openvpms.archetype.rules.finance.account.FinancialTestHelper;
import org.openvpms.archetype.rules.product.ProductArchetypes;
import org.openvpms.archetype.test.TestHelper;
import org.openvpms.component.business.domain.im.act.FinancialAct;
import org.openvpms.component.business.domain.im.common.EntityRelationship;
import org.openvpms.component.business.domain.im.datatypes.quantity.Money;
import org.openvpms.component.business.domain.im.party.Party;
import org.openvpms.component.business.domain.im.product.Product;
import org.openvpms.component.business.service.archetype.helper.ActBean;
import org.openvpms.component.business.service.archetype.helper.EntityBean;
import org.openvpms.component.business.service.archetype.helper.IMObjectBean;
import org.openvpms.component.business.service.archetype.helper.TypeHelper;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

/**
 * Tests the {@link ChargeStockUpdater} class, when invoked by the
 * <em>archetypeService.save.act.customerAccountInvoiceItem.before</em>,
 * <em>archetypeService.remove.act.customerAccountChargesInvoice.before</em>,
 * <em>archetypeService.remove.act.customerAccountInvoiceItem.before</em>,
 * <em>archetypeService.save.act.customerAccountCreditItem.before</em>,
 * <em>archetypeService.remove.act.customerAccountChargesCredit.before</em>,
 * <em>archetypeService.remove.act.customerAccountCreditItem.before</em>,
 * <em>archetypeService.save.act.customerAccountCounterItem.before</em>,
 * <em>archetypeService.remove.act.customerAccountChargesCounter.before</em> and
 * <em>archetypeService.remove.act.customerAccountCounterItem.before</em>
 * rules.
 *
 * @author Tim Anderson
 */
public class ChargeStockUpdaterTestCase extends AbstractStockTest {

    /**
     * The product.
     */
    private Product product;

    /**
     * The customer.
     */
    private Party customer;

    /**
     * The patient.
     */
    private Party patient;

    /**
     * The stock location.
     */
    private Party stockLocation;

    /**
     * The transaction manager.
     */
    private PlatformTransactionManager txnManager;

    /**
     * Verifies that stock is updated for an invoice.
     */
    @Test
    public void testInvoiceStockUpdate() {
        checkStockUpdate(createInvoice());
    }

    /**
     * Verifies that stock is updated when an invoice is removed.
     */
    @Test
    public void testInvoiceRemoval() {
        checkChargeRemoval(createInvoice());
    }

    /**
     * Verifies that stock is updated when an invoice item is removed.
     */
    @Test
    public void testInvoiceItemRemoval() {
        checkItemRemoval(createInvoice());
    }

    /**
     * Verifies that stock is updated for a counter charge.
     */
    @Test
    public void testCounterChargeStockUpdate() {
        checkStockUpdate(createCounterCharge());
    }

    /**
     * Verifies that stock is updated when a counter charge item is removed.
     */
    @Test
    public void testCounterChargeRemoval() {
        checkChargeRemoval(createCounterCharge());
    }

    /**
     * Verifies that stock is updated when a counter charge item is removed.
     */
    @Test
    public void testCounterChargeItemRemoval() {
        checkItemRemoval(createCounterCharge());
    }

    /**
     * Verifies that stock is updated for a credit charge.
     */
    @Test
    public void testCreditChargeStockUpdate() {
        checkStockUpdate(createCreditCharge());
    }

    /**
     * Verifies that stock is updated when a credit charge is removed.
     */
    @Test
    public void testCreditChargeRemoval() {
        checkChargeRemoval(createCreditCharge());
    }

    /**
     * Verifies that stock is updated when a credit charge item is removed.
     */
    @Test
    public void testCreditChargeItemRemoval() {
        checkItemRemoval(createCreditCharge());
    }

    /**
     * Verifies that stock is updated correctly if a charge is saved multiple times in a transaction.
     */
    @Test
    public void testMultipleSaveInTxn() {
        final List<FinancialAct> acts = createInvoice();
        FinancialAct item = acts.get(1);
        BigDecimal initialQuantity = BigDecimal.ZERO;
        BigDecimal quantity = BigDecimal.valueOf(5);

        item.setQuantity(quantity);

        checkEquals(initialQuantity, getStock(stockLocation, product));
        BigDecimal expected = getQuantity(initialQuantity, quantity, false);

        TransactionTemplate template = new TransactionTemplate(txnManager);
        template.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                save(acts);
                save(acts);
            }
        });
        checkEquals(expected, getStock(stockLocation, product));

        save(acts); // stock shouldn't change if resaved
        checkEquals(expected, getStock(stockLocation, product));
    }

    /**
     * Verifies that the stock is updated correctly if referred to by two different items in a transaction.
     */
    @Test
    public void testMultipleStockUpdatesInTxn() {
        final List<FinancialAct> acts = new ArrayList<FinancialAct>(createInvoice());
        final FinancialAct item1 = acts.get(1);
        final FinancialAct item2 = FinancialTestHelper.createChargeItem(CustomerAccountArchetypes.INVOICE_ITEM,
                patient, product, BigDecimal.ONE);
        addStockLocation(item2);
        acts.add(item2);

        BigDecimal initialQuantity = BigDecimal.ZERO;
        BigDecimal quantity = BigDecimal.valueOf(5);

        item1.setQuantity(quantity);
        item2.setQuantity(quantity);

        checkEquals(initialQuantity, getStock(stockLocation, product));
        BigDecimal expected = getQuantity(initialQuantity, quantity.add(quantity), false);

        TransactionTemplate template = new TransactionTemplate(txnManager);
        template.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                save(acts);
            }
        });
        checkEquals(expected, getStock(stockLocation, product));

        template.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                item1.setQuantity(BigDecimal.ONE);
                save(item1);
                remove(item2);
            }
        });
        expected = getQuantity(initialQuantity, BigDecimal.ONE, false);
        checkEquals(expected, getStock(stockLocation, product));
    }

    /**
     * Verifies that stock is updated correctly if an item is saved twice in the one transaction, but the first
     * save is incomplete.
     */
    @Test
    public void testPartialSaveInTxn() {
        final List<FinancialAct> acts = createInvoice();
        final FinancialAct invoice = acts.get(0);
        final FinancialAct item = acts.get(1);
        BigDecimal initialQuantity = BigDecimal.ZERO;
        BigDecimal quantity = BigDecimal.valueOf(5);

        item.setQuantity(quantity);

        checkEquals(initialQuantity, getStock(stockLocation, product));
        BigDecimal expected = getQuantity(initialQuantity, quantity, false);

        TransactionTemplate template = new TransactionTemplate(txnManager);
        template.execute(new TransactionCallbackWithoutResult() {
            @Override
            protected void doInTransactionWithoutResult(TransactionStatus status) {
                save(item);
                save(item);
                save(invoice);
            }
        });
        checkEquals(expected, getStock(stockLocation, product));

        save(acts); // stock shouldn't change if resaved
        checkEquals(expected, getStock(stockLocation, product));
    }

    /**
     * Sets up the test case.
     */
    @Before
    public void setUp() {
        product = TestHelper.createProduct();
        customer = TestHelper.createCustomer();
        patient = TestHelper.createPatient();
        stockLocation = createStockLocation();
        txnManager = applicationContext.getBean(PlatformTransactionManager.class);
    }

    /**
     * Verifies that stock is updated when for a charge.
     *
     * @param acts the charge act and item
     */
    private void checkStockUpdate(List<FinancialAct> acts) {
        FinancialAct item = acts.get(1);
        BigDecimal initialQuantity = BigDecimal.ZERO;
        BigDecimal quantity = BigDecimal.valueOf(5);
        Product product2 = TestHelper.createProduct();

        boolean credit = TypeHelper.isA(item, CustomerAccountArchetypes.CREDIT_ITEM);
        BigDecimal expected = getQuantity(initialQuantity, quantity, credit);

        item.setQuantity(quantity);

        checkEquals(initialQuantity, getStock(stockLocation, product));

        save(acts);
        checkEquals(expected, getStock(stockLocation, product));

        save(acts); // stock shouldn't change if resaved
        checkEquals(expected, getStock(stockLocation, product));

        item = get(item);
        BigDecimal updatedQty = new BigDecimal(10);
        item.setQuantity(updatedQty);
        expected = getQuantity(initialQuantity, updatedQty, credit);
        save(item);
        checkEquals(expected, getStock(stockLocation, product));

        item = get(item);
        save(item);
        checkEquals(expected, getStock(stockLocation, product));

        ActBean itemBean = new ActBean(item);
        itemBean.setParticipant(ProductArchetypes.PRODUCT_PARTICIPATION, product2);
        save(item);
        checkEquals(BigDecimal.ZERO, getStock(stockLocation, product));
        checkEquals(expected, getStock(stockLocation, product2));
    }

    /**
     * Verifies that stock is updated when a charge is removed.
     *
     * @param acts the charge act and item
     */
    private void checkChargeRemoval(List<FinancialAct> acts) {
        BigDecimal quantity = new BigDecimal(10);
        FinancialAct act = acts.get(0);
        FinancialAct item = acts.get(1);
        item.setQuantity(quantity);

        save(acts);

        BigDecimal expected = getQuantity(BigDecimal.ZERO, quantity, act.isCredit());

        checkEquals(expected, getStock(stockLocation, product));

        // now remove the charge
        remove(act);

        checkEquals(BigDecimal.ZERO, getStock(stockLocation, product));
    }

    private void checkItemRemoval(List<FinancialAct> acts) {
        BigDecimal quantity = new BigDecimal(10);
        FinancialAct item = acts.get(1);
        item.setQuantity(quantity);

        save(acts);

        boolean credit = TypeHelper.isA(item, CustomerAccountArchetypes.CREDIT_ITEM);

        BigDecimal expected = getQuantity(BigDecimal.ZERO, quantity, credit);

        checkEquals(expected, getStock(stockLocation, product));
        remove(item);

        checkEquals(BigDecimal.ZERO, getStock(stockLocation, product));
    }

    private List<FinancialAct> createInvoice() {
        List<FinancialAct> acts = FinancialTestHelper.createChargesInvoice(new Money(100), customer, patient,
                product, ActStatus.IN_PROGRESS);
        addStockLocation(acts.get(1));
        return acts;
    }

    private List<FinancialAct> createCreditCharge() {
        List<FinancialAct> acts = FinancialTestHelper.createChargesCredit(new Money(100), customer, patient,
                product, ActStatus.IN_PROGRESS);
        addStockLocation(acts.get(1));
        return acts;
    }

    private List<FinancialAct> createCounterCharge() {
        List<FinancialAct> acts = FinancialTestHelper.createChargesCounter(new Money(100), customer, product,
                ActStatus.IN_PROGRESS);
        addStockLocation(acts.get(1));
        return acts;
    }

    private void addStockLocation(FinancialAct item) {
        ActBean bean = new ActBean(item);
        bean.addParticipation(StockArchetypes.STOCK_LOCATION_PARTICIPATION, stockLocation);
    }

    private BigDecimal getQuantity(BigDecimal current, BigDecimal change, boolean credit) {
        return (credit) ? current.add(change) : current.subtract(change);
    }

    private BigDecimal getStock(Party location, Product product) {
        product = get(product);
        EntityBean prodBean = new EntityBean(product);
        EntityRelationship rel = prodBean.getRelationship(location);
        if (rel != null) {
            IMObjectBean relBean = new IMObjectBean(rel);
            return relBean.getBigDecimal("quantity");
        }
        return BigDecimal.ZERO;
    }

}