org.openvpms.archetype.rules.party.CustomerMergerTestCase.java Source code

Java tutorial

Introduction

Here is the source code for org.openvpms.archetype.rules.party.CustomerMergerTestCase.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 2015 (C) OpenVPMS Ltd. All Rights Reserved.
 */

package org.openvpms.archetype.rules.party;

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.CustomerAccountQueryFactory;
import org.openvpms.archetype.rules.finance.account.CustomerAccountRules;
import org.openvpms.archetype.rules.finance.account.FinancialTestHelper;
import org.openvpms.archetype.rules.finance.statement.EndOfPeriodProcessor;
import org.openvpms.archetype.rules.patient.PatientRules;
import org.openvpms.archetype.rules.util.DateUnits;
import org.openvpms.archetype.test.TestHelper;
import org.openvpms.component.business.domain.im.act.Act;
import org.openvpms.component.business.domain.im.act.FinancialAct;
import org.openvpms.component.business.domain.im.common.EntityIdentity;
import org.openvpms.component.business.domain.im.datatypes.quantity.Money;
import org.openvpms.component.business.domain.im.lookup.Lookup;
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.ArchetypeServiceException;
import org.openvpms.component.system.common.query.ArchetypeQuery;
import org.openvpms.component.system.common.query.IMObjectQueryIterator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;

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

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.openvpms.archetype.test.TestHelper.getDate;
import static org.openvpms.archetype.test.TestHelper.getDatetime;

/**
 * Tests the {@link CustomerMerger} class.
 *
 * @author Tim Anderson
 */
public class CustomerMergerTestCase extends AbstractPartyMergerTest {

    /**
     * Customer rules.
     */
    private CustomerRules customerRules;

    /**
     * Patient rules.
     */
    @Autowired
    private PatientRules patientRules;

    /**
     * The customer account rules.
     */
    @Autowired
    private CustomerAccountRules customerAccountRules;

    /**
     * The practice.
     */
    private Party practice;

    /**
     * The transaction template.
     */
    private TransactionTemplate template;

    /**
     * Merges two customers and verifies the merged customer contains the
     * contacts of both.
     */
    @Test
    public void testMergeContacts() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createCustomer();

        int fromContactsSize = from.getContacts().size();
        int toContactsSize = to.getContacts().size();

        Party merged = checkMerge(from, to);

        // verify contacts copied across
        assertEquals(fromContactsSize + toContactsSize, merged.getContacts().size());
    }

    /**
     * Tests a merge where the merge-from customer has an account type,
     * and the merge-to customer doesn't.
     */
    @Test
    public void testMergeAccountType() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createCustomer();
        Lookup accountType = FinancialTestHelper.createAccountType(30, DateUnits.DAYS, BigDecimal.ZERO);

        from.addClassification(accountType);

        Party merged = checkMerge(from, to);
        assertEquals(accountType, customerRules.getAccountType(merged));
    }

    /**
     * Tests a merge where both customers have different account types.
     * The merge-to customer's account type should take precedence.
     */
    @Test
    public void testMergeAccountTypes() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createCustomer();
        Lookup accountType1 = FinancialTestHelper.createAccountType(30, DateUnits.DAYS, BigDecimal.ZERO);
        Lookup accountType2 = FinancialTestHelper.createAccountType(15, DateUnits.DAYS, BigDecimal.ZERO);

        from.addClassification(accountType1);
        to.addClassification(accountType2);

        Lookup fromAccountType = customerRules.getAccountType(from);
        Lookup toAccountType = customerRules.getAccountType(to);

        assertEquals(accountType1, fromAccountType);
        assertEquals(accountType2, toAccountType);

        Party merged = checkMerge(from, to);

        assertEquals(accountType2, customerRules.getAccountType(merged));
    }

    /**
     * Tests that entity relationships are copied.
     */
    @Test
    public void testMergeEntityRelationships() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createCustomer();
        Party patient1 = TestHelper.createPatient(from, true);
        Party patient2 = TestHelper.createPatient(to, true);

        Party merged = checkMerge(from, to);

        patient1 = get(patient1);
        patient2 = get(patient2);

        assertTrue(patientRules.isOwner(merged, patient1));
        assertTrue(patientRules.isOwner(merged, patient2));
    }

    /**
     * Tests that entity identities are copied.
     */
    @Test
    public void testMergeEntityIdentities() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createCustomer();

        EntityIdentity id1 = createIdentity("ABC1234");
        from.addIdentity(id1);

        EntityIdentity id2 = createIdentity("XYZ1234");
        to.addIdentity(id2);

        Party merged = checkMerge(from, to);

        EntityIdentity[] identities = merged.getIdentities()
                .toArray(new EntityIdentity[merged.getIdentities().size()]);
        String idA = identities[0].getIdentity();
        String idB = identities[1].getIdentity();
        assertTrue(id1.getIdentity().equals(idA) || id1.getIdentity().equals(idB));
        assertTrue(id2.getIdentity().equals(idA) || id2.getIdentity().equals(idB));
    }

    /**
     * Verifies that participations are moved to the merged customer.
     */
    @Test
    public void testMergeParticipations() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createCustomer();
        Party patient = TestHelper.createPatient();
        Product product = TestHelper.createProduct();

        assertEquals(0, countParticipations(from));
        assertEquals(0, countParticipations(to));

        for (int i = 0; i < 10; ++i) {
            List<FinancialAct> invoice = FinancialTestHelper.createChargesInvoice(new Money(100), from, patient,
                    product, ActStatus.POSTED);
            save(invoice);
        }
        int fromRefs = countParticipations(from);
        assertTrue(fromRefs >= 10);

        checkMerge(from, to);

        // verify the participations no longer reference the from customer
        assertEquals(0, countParticipations(from));

        // verify all participations moved to the 'to' customer
        int toRefs = countParticipations(to);
        assertEquals(toRefs, fromRefs); //
    }

    /**
     * Verifies that only <em>party.customerperson</em> instances can be merged.
     */
    @Test
    public void testMergeInvalidParty() {
        Party from = TestHelper.createCustomer();
        Party to = TestHelper.createSupplier();
        try {
            checkMerge(from, to);
            fail("Expected merge to invalid party to fail");
        } catch (MergeException expected) {
            assertEquals(MergeException.ErrorCode.InvalidType, expected.getErrorCode());
        }
    }

    /**
     * Verifies that a customer cannot be merged with itself.
     */
    @Test
    public void testMergeToSameCustomer() {
        Party from = TestHelper.createCustomer();
        try {
            checkMerge(from, from);
            fail("Expected merge to same customer to fail");
        } catch (MergeException expected) {
            assertEquals(MergeException.ErrorCode.CannotMergeToSameObject, expected.getErrorCode());
        }
    }

    /**
     * Verifies that the 'to' customer includes the 'from' customer's balance
     * after the merge, and that any opening and closing balance acts on or
     * after the first transaction of the from customer are removed.
     */
    @Test
    public void testMergeAccounts() {
        final Money eighty = new Money(80);
        final Money forty = new Money(40);
        final Money fifty = new Money(50);
        final Money ninety = new Money(90);
        Party from = TestHelper.createCustomer();
        Party fromPatient = TestHelper.createPatient();
        Party to = TestHelper.createCustomer();
        Party toPatient = TestHelper.createPatient();
        Product product = TestHelper.createProduct();

        // add some transaction history for the 'from' customer
        Date firstStartTime = getDatetime("2007-01-02 10:0:0");
        addInvoice(firstStartTime, eighty, from, fromPatient, product);
        addPayment(getDatetime("2007-01-02 11:0:0"), forty, from);

        runEOP(from, getDate("2007-02-01"));

        // ... and the 'to' customer
        addInvoice(getDatetime("2007-01-01 10:0:0"), fifty, to, toPatient, product);
        runEOP(to, getDate("2007-01-01"));
        runEOP(to, getDate("2007-02-01"));

        // verify balances prior to merge
        assertEquals(0, forty.compareTo(customerAccountRules.getBalance(from)));
        assertEquals(0, fifty.compareTo(customerAccountRules.getBalance(to)));

        to = checkMerge(from, to);

        // verify balances after merge
        assertEquals(0, BigDecimal.ZERO.compareTo(customerAccountRules.getBalance(from)));
        assertEquals(0, ninety.compareTo(customerAccountRules.getBalance(to)));

        // now verify that the only opening and closing balance acts for the
        // to customer are prior to the first act of the from customer
        ArchetypeQuery query = CustomerAccountQueryFactory.createQuery(to, new String[] {
                CustomerAccountArchetypes.OPENING_BALANCE, CustomerAccountArchetypes.CLOSING_BALANCE });
        IMObjectQueryIterator<Act> iter = new IMObjectQueryIterator<Act>(query);
        int count = 0;
        while (iter.hasNext()) {
            Act act = iter.next();
            long startTime = act.getActivityStartTime().getTime();
            assertTrue(startTime < firstStartTime.getTime());
            ++count;
        }
        assertEquals(2, count); // expect a closing and opening balance

        // verify there are no acts associated with the removed 'from' customer
        assertEquals(0, countParticipations(from));
    }

    /**
     * Sets up the test case.
     */
    @Before
    public void setUp() {
        PlatformTransactionManager mgr = applicationContext.getBean(PlatformTransactionManager.class);
        template = new TransactionTemplate(mgr);

        customerRules = new CustomerRules(getArchetypeService(), getLookupService());
        practice = (Party) create("party.organisationPractice");
    }

    /**
     * Runs end of period for a customer.
     *
     * @param customer      the customer
     * @param statementDate the statement date
     */
    private void runEOP(Party customer, Date statementDate) {
        EndOfPeriodProcessor eop = new EndOfPeriodProcessor(statementDate, true, practice, getArchetypeService(),
                getLookupService(), customerAccountRules);
        eop.process(customer);
    }

    /**
     * Merges two customers in a transaction, and verifies the 'from' customer
     * has been deleted.
     *
     * @param from the customer to merge from
     * @param to   the customer to merge to
     * @return the merged customer
     */
    private Party checkMerge(final Party from, final Party to) {
        template.execute(new TransactionCallback<Object>() {
            public Object doInTransaction(TransactionStatus transactionStatus) {
                customerRules.mergeCustomers(from, to);
                return null;
            }
        });

        // verify the from customer has been deleted
        assertNull(get(from));

        Party merged = get(to);
        assertNotNull(merged);
        return merged;
    }

    /**
     * Saves an invoice for a customer.
     *
     * @param startTime the invoice start time
     * @param amount    the invoice amount
     * @param customer  the customer
     * @param patient   the patient
     * @param product   the product
     */
    private void addInvoice(Date startTime, Money amount, Party customer, Party patient, Product product) {
        List<FinancialAct> acts = FinancialTestHelper.createChargesInvoice(amount, customer, patient, product,
                ActStatus.POSTED);
        FinancialAct act = acts.get(0);
        act.setActivityStartTime(startTime);
        save(acts);
    }

    /**
     * Saves a payment for a customer.
     *
     * @param startTime the payment start time
     * @param amount    the payment amount
     * @param customer  the customer
     */
    private void addPayment(Date startTime, Money amount, Party customer) {
        Act act = FinancialTestHelper.createPaymentCash(amount, customer, FinancialTestHelper.createTill());
        act.setActivityStartTime(startTime);
        save(act);
    }

    /**
     * Helper to create a new entity identity.
     *
     * @param identity the identity
     * @return a new entity identity
     * @throws ArchetypeServiceException for any error
     */
    private EntityIdentity createIdentity(String identity) {
        EntityIdentity id = (EntityIdentity) create("entityIdentity.code");
        id.setIdentity(identity);
        return id;
    }
}