예제 #1
0
파일: des_test.py 프로젝트: kthnyt/auxi.0
 def setUp(self):
     self.structure = GeneralLedgerStructure("NameA",
                                             description="DescriptionA")
     self.structure["Retained Income"].create_account(
         "TestA", description="TestA_Desc", number="010")
     self.object = GeneralLedger("NameA",
                                 self.structure,
                                 description="DescriptionA")
예제 #2
0
 def __init__(self, name, gl_structure, description=None):
     self.gl = GeneralLedger("General Ledger",
                             gl_structure,
                             description="General Ledger")
     self._parent_path = ""
     self.path = name
     self.components = []
     self.negative_income_tax_total = 0
     self._prev_year_end_datetime = datetime.min
     self._curr_year_end_datetime = datetime.min
     self._exec_year_end_datetime = datetime.min
     self.period_count = -1
     super(Entity, self).__init__(name, description=description)
예제 #3
0
    def setUp(self):
        gls = Gls("NameA")
        self.gl = GeneralLedger("NameA", gls)
        self.object = TransactionList(
            data_source=self.gl,
            start=datetime(2016, 4, 1),
            end=datetime(2016, 8, 1), output_path=None)

        self.gl.create_transaction(
            "tx1",
            description='1',
            tx_date=datetime(2016, 2, 1),
            dt_account="Bank\Default1",
            cr_account="Sales\Default1",
            source="test1",
            amount=1001)
        self.gl.create_transaction(
            "tx2",
            description='2',
            tx_date=datetime(2016, 3, 1),
            dt_account="Bank\Default2",
            cr_account="Sales\Default2",
            source="test2",
            amount=1002)
        self.gl.create_transaction(
            "tx3",
            description='3',
            tx_date=datetime(2016, 4, 2),
            dt_account="Bank\Default3",
            cr_account="Sales\Default3",
            source="test3",
            amount=1003)
        self.gl.create_transaction(
            "tx4",
            description='4',
            tx_date=datetime(2016, 5, 1),
            dt_account="Bank\Default4",
            cr_account="Sales\Default4",
            source="test4",
            amount=1004)
        self.gl.create_transaction(
            "tx5",
            description='5',
            tx_date=datetime(2016, 9, 1),
            dt_account="Bank\Default5",
            cr_account="Sales\Default5",
            source="test5",
            amount=1005)
예제 #4
0
파일: des_test.py 프로젝트: kthnyt/auxi.0
class GeneralLedgerUnitTester(unittest.TestCase):
    """
    Tester for the auxi.modelling.financial.des.GeneralLedger class.
    """
    def setUp(self):
        self.structure = GeneralLedgerStructure("NameA",
                                                description="DescriptionA")
        self.structure["Retained Income"].create_account(
            "TestA", description="TestA_Desc", number="010")
        self.object = GeneralLedger("NameA",
                                    self.structure,
                                    description="DescriptionA")

    def test_constructor(self):
        self.assertEqual(self.object.name, "NameA")
        self.assertEqual(self.object.description, "DescriptionA")
        self.assertEqual(self.structure, self.object.structure)

    def test_create_transaction(self):
        new_tx = self.object.create_transaction("TestA",
                                                description="TestA_Desc",
                                                tx_date=datetime(2016, 2,
                                                                 1).date(),
                                                dt_account="Bank",
                                                cr_account="Sales",
                                                source="Peanut Sales",
                                                amount=20.00)

        tx_list = self.object.transactions
        self.assertEqual(new_tx.name, tx_list[0].name)
        self.assertEqual(new_tx.tx_date, tx_list[0].tx_date)
        self.assertEqual(new_tx.dt_account, tx_list[0].dt_account)
        self.assertEqual(new_tx.cr_account, tx_list[0].cr_account)
        self.assertEqual(new_tx.source, tx_list[0].source)
        self.assertEqual(new_tx.amount, tx_list[0].amount)
예제 #5
0
 def setUp(self):
     self.structure = GeneralLedgerStructure("NameA",
                                             description="DescriptionA")
     self.structure["Retained Income"].create_account(
         "TestA",
         description="TestA_Desc",
         number="010")
     self.object = GeneralLedger("NameA",
                                 self.structure,
                                 description="DescriptionA")
예제 #6
0
 def __init__(self, name, gl_structure, description=None):
     self.gl = GeneralLedger(
         "General Ledger",
         gl_structure,
         description="General Ledger")
     self._parent_path = ""
     self.path = name
     self.components = []
     self.negative_income_tax_total = 0
     self._prev_year_end_datetime = datetime.min
     self._curr_year_end_datetime = datetime.min
     self._exec_year_end_datetime = datetime.min
     self.period_count = -1
     super(Entity, self).__init__(name, description=description)
예제 #7
0
    def test_run(self):
        """
        Test that the activity run method creates a transaction with an amount
        of 5000.
        """

        structure = GeneralLedgerStructure("NameA", description="DescriptionA")
        gl = GeneralLedger("NameA", structure, description="DescriptionA")
        clock = Clock("NameA", start_datetime=datetime(2016, 1, 1))
        clock.tick()
        clock.tick()
        self.object.prepare_to_run(clock, 20)
        self.object.run(clock, gl)
        self.assertEqual(len(gl.transactions), 1)
        self.assertEqual(gl.transactions[0].amount, 5000)
예제 #8
0
    def setUp(self):
        gls = Gls("NameA")
        self.gl = GeneralLedger("NameA", gls)
        self.object = TransactionList(
            data_source=self.gl,
            start=datetime(2016, 4, 1),
            end=datetime(2016, 8, 1), output_path=None)

        self.gl.create_transaction(
            "tx1",
            description='1',
            tx_date=datetime(2016, 2, 1),
            dt_account="Bank\Default1",
            cr_account="Sales\Default1",
            source="test1",
            amount=1001)
        self.gl.create_transaction(
            "tx2",
            description='2',
            tx_date=datetime(2016, 3, 1),
            dt_account="Bank\Default2",
            cr_account="Sales\Default2",
            source="test2",
            amount=1002)
        self.gl.create_transaction(
            "tx3",
            description='3',
            tx_date=datetime(2016, 4, 2),
            dt_account="Bank\Default3",
            cr_account="Sales\Default3",
            source="test3",
            amount=1003)
        self.gl.create_transaction(
            "tx4",
            description='4',
            tx_date=datetime(2016, 5, 1),
            dt_account="Bank\Default4",
            cr_account="Sales\Default4",
            source="test4",
            amount=1004)
        self.gl.create_transaction(
            "tx5",
            description='5',
            tx_date=datetime(2016, 9, 1),
            dt_account="Bank\Default5",
            cr_account="Sales\Default5",
            source="test5",
            amount=1005)
예제 #9
0
 def test_run(self):
     """
     Test that the component runs its activities.
     """
     # Set up the general ledger so that an activity can create a
     # transaction.
     structure = GeneralLedgerStructure("NameA", description="DescriptionA")
     gl = GeneralLedger("NameA", structure, description="DescriptionA")
     # Prepare the component for a run. Tick the clock to the correct place
     # for the activity to create a transaction.
     clock = Clock("NameA", start_datetime=datetime(2016, 1, 1))
     clock.tick()
     clock.tick()
     self.object.prepare_to_run(clock, 20)
     self.object.run(clock, gl)
     # If the activity has been run, then a transaction should have
     # been generated.
     self.assertEqual(len(gl.transactions), 1)
예제 #10
0
 def setUp(self):
     self.gl_structure = GeneralLedgerStructure("NameA",
                                                description="DescriptionA")
     self.gl_structure["Sales"].create_account("Default", number="0000")
     self.gl = GeneralLedger("NameA",
                             self.gl_structure,
                             description="DescriptionA")
     self.object = Component("ComponentA",
                             self.gl,
                             description="DescriptionA")
     # Set up the needed objects
     self.object.create_component("ComponentA1", description="ca1")
     self.object.create_component("ComponentA2", description="ca2")
     self.basic_activity = BasicActivity("BasicActivityA",
                                         description="DescriptionA",
                                         dt_account="Bank/Default",
                                         cr_account="Sales/Default",
                                         amount=5000,
                                         start=datetime(2016, 2, 1),
                                         end=datetime(2017, 2, 1),
                                         interval=3)
     self.object["ComponentA1"].add_activity(self.basic_activity)
예제 #11
0
class GeneralLedgerUnitTester(unittest.TestCase):
    """
    Tester for the auxi.modelling.financial.des.GeneralLedger class.
    """

    def setUp(self):
        self.structure = GeneralLedgerStructure("NameA",
                                                description="DescriptionA")
        self.structure["Retained Income"].create_account(
            "TestA",
            description="TestA_Desc",
            number="010")
        self.object = GeneralLedger("NameA",
                                    self.structure,
                                    description="DescriptionA")

    def test_constructor(self):
        self.assertEqual(self.object.name, "NameA")
        self.assertEqual(self.object.description, "DescriptionA")
        self.assertEqual(self.structure, self.object.structure)

    def test_create_transaction(self):
        new_tx = self.object.create_transaction(
            "TestA",
            description="TestA_Desc",
            tx_date=datetime(2016, 2, 1).date(),
            dt_account="Bank",
            cr_account="Sales",
            source="Peanut Sales",
            amount=20.00)

        tx_list = self.object.transactions
        self.assertEqual(new_tx.name, tx_list[0].name)
        self.assertEqual(new_tx.tx_date, tx_list[0].tx_date)
        self.assertEqual(new_tx.dt_account, tx_list[0].dt_account)
        self.assertEqual(new_tx.cr_account, tx_list[0].cr_account)
        self.assertEqual(new_tx.source, tx_list[0].source)
        self.assertEqual(new_tx.amount, tx_list[0].amount)
예제 #12
0
    def setUp(self):
        self.gl_structure = GeneralLedgerStructure(
            "GL Structure", description="General Ledger Structure")
        self.gl_structure["Account Payable"].create_account(
            "Capital Loan", "0000")
        self.gl_structure["Expense"].create_account("Interest Expense", "0000")

        self.gl = GeneralLedger("GL",
                                self.gl_structure,
                                description="General Ledger")

        self.clock = Clock("NameA", start_datetime=datetime(2016, 2, 1))

        self.object = BasicLoanActivity(
            "Capital Loan",
            bank_account="Bank/Default",
            loan_account="Account Payable/Capital Loan",
            interest_account="Expense/Interest Expense",
            amount=180000,
            interest_rate=0.15,
            start=datetime(2016, 2, 1),
            duration=36,
            interval=1,
            description="Loan for Capital")
예제 #13
0
class Entity(NamedObject):
    """
    Represents an entity class. An entity consists of business components
    e.g. Sales department. It executes its components and performs
    financial year end transcations.

    :param name: The name.
    :param gl_structure: The general ledger structure the entity's
      general ledger will be initialized with.
    :param description: The description.
    :param period_count: The number of periods the entity should be run for.
    """

    def __init__(self, name, gl_structure, description=None):
        self.gl = GeneralLedger(
            "General Ledger",
            gl_structure,
            description="General Ledger")
        self._parent_path = ""
        self.path = name
        self.components = []
        self.negative_income_tax_total = 0
        self._prev_year_end_datetime = datetime.min
        self._curr_year_end_datetime = datetime.min
        self._exec_year_end_datetime = datetime.min
        self.period_count = -1
        super(Entity, self).__init__(name, description=description)

    def __getitem__(self, key):
        return [c for c in self.components if c.name == key][0]

    def set_parent_path(self, value):
        """
        Set the parent path and the path from the new parent path.

        :param value: The path to the object's parent
        """

        self._parent_path = value
        self.path = value + r'/' + self.name
        self._update_childrens_parent_path()

    def _update_childrens_parent_path(self):
        for c in self.components:
            c.set_parent_path(self.path)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        self.path = self._parent_path + r'/' + self.name
        self._update_childrens_parent_path()

    def create_component(self, name, description=None):
        """
        Create a component in the business entity.

        :param name: The component's name.
        :param description: The component's description.

        :returns: The created component.
        """

        new_comp = Component(name, self.gl, description=description)
        new_comp.set_parent_path(self.path)
        self.components.append(new_comp)
        return new_comp

    def remove_component(self, name):
        """
        Remove a component from the entity.

        :param name: The name of the component to remove.
        """

        component_to_remove = None
        for c in self.components:
            if c.name == name:
                component_to_remove = c
        if component_to_remove is not None:
            self.components.remove(component_to_remove)

    def prepare_to_run(self, clock, period_count):
        """
        Prepare the entity for execution.

        :param clock: The clock containing the execution start time and
          execution period information.
        :param period_count: The total amount of periods this activity will be
          requested to be run for.
        """

        self.period_count = period_count

        self._exec_year_end_datetime = clock.get_datetime_at_period_ix(
            period_count)
        self._prev_year_end_datetime = clock.start_datetime
        self._curr_year_end_datetime = clock.start_datetime + relativedelta(
            years=1)

        # Remove all the transactions
        del self.gl.transactions[:]

        for c in self.components:
            c.prepare_to_run(clock, period_count)

        self.negative_income_tax_total = 0

    def run(self, clock):
        """
        Execute the entity at the current clock cycle.

        :param clock: The clock containing the current execution time and
          period information.
        """

        if clock.timestep_ix >= self.period_count:
            return

        for c in self.components:
            c.run(clock, self.gl)

        self._perform_year_end_procedure(clock)

    def _perform_year_end_procedure(self, clock):
        if clock.get_datetime() >= self._curr_year_end_datetime\
         or clock.timestep_ix == self.period_count:
            gls = self.gl.structure
            year_start_date = self._prev_year_end_datetime
            year_end_date = self._curr_year_end_datetime + \
                timedelta(seconds=-1)

            self._prev_year_end_datetime = self._curr_year_end_datetime
            self._curr_year_end_datetime += relativedelta(years=1)
            if self._curr_year_end_datetime > self._exec_year_end_datetime:
                self._curr_year_end_datetime = self._exec_year_end_datetime

            gross_profit_write_off_accs = OrderedDict()
            inc_summary_write_off_accs = OrderedDict()
            sales_accs = gls.get_account_decendants(gls._acci_sales_)
            cost_of_sales_accs = gls.get_account_decendants(gls._acci_cos_)
            year_taxes = []
            for tx in self.gl.transactions:
                if tx.tx_date >= year_start_date and\
                 tx.tx_date <= year_end_date:
                    year_taxes.append(tx)
            for tx in year_taxes:
                cr_acc = gls.get_account(tx.cr_account)
                dt_acc = gls.get_account(tx.dt_account)

                if cr_acc in sales_accs:
                    if cr_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[cr_acc] += tx.amount
                    else:
                        gross_profit_write_off_accs[cr_acc] = tx.amount
                elif dt_acc in sales_accs:
                    if dt_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[dt_acc] -= tx.amount
                    else:
                        gross_profit_write_off_accs[dt_acc] = -tx.amount

                elif cr_acc in cost_of_sales_accs:
                    if cr_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[cr_acc] += tx.amount
                    else:
                        gross_profit_write_off_accs[cr_acc] = tx.amount
                elif dt_acc in cost_of_sales_accs:
                    if dt_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[dt_acc] -= tx.amount
                    else:
                        gross_profit_write_off_accs[dt_acc] = -tx.amount

                elif cr_acc.account_type == AccountType.revenue:
                    if cr_acc in inc_summary_write_off_accs:
                        inc_summary_write_off_accs[cr_acc] += tx.amount
                    else:
                        inc_summary_write_off_accs[cr_acc] = tx.amount
                elif dt_acc.account_type == AccountType.revenue:
                    if dt_acc.account_type == AccountType.revenue:
                        inc_summary_write_off_accs[dt_acc] -= tx.amount
                    else:
                        inc_summary_write_off_accs[dt_acc] = -tx.amount

                elif cr_acc.account_type == AccountType.expense:
                    if cr_acc in inc_summary_write_off_accs:
                        inc_summary_write_off_accs[cr_acc] -= tx.amount
                    else:
                        inc_summary_write_off_accs[cr_acc] = -tx.amount
                elif dt_acc.account_type == AccountType.expense:
                    if dt_acc in inc_summary_write_off_accs:
                        inc_summary_write_off_accs[dt_acc] += tx.amount
                    else:
                        inc_summary_write_off_accs[dt_acc] = tx.amount

            inc_sum = self._perform_year_end_gross_profit_and_income_summary(
                year_end_date,
                gross_profit_write_off_accs,
                inc_summary_write_off_accs)
            inc_sum = self._perform_year_end_income_tax(
                year_end_date,
                inc_sum)
            self._perform_year_end_retained_earnings(
                year_end_date,
                inc_sum)

    def _perform_year_end_gross_profit_and_income_summary(
            self,
            year_end_datetime,
            gross_profit_write_off_accounts,
            income_summary_write_off_accounts):
        gls = self.gl.structure
        gross_profit = self._perform_year_end_gross_profit(
            year_end_datetime,
            gross_profit_write_off_accounts)
        income_summary_amount = self._perform_year_end_income_summary(
            year_end_datetime,
            gross_profit,
            income_summary_write_off_accounts)

        def create_tx(dt_account, cr_account):
            tx_name = "Settle the '" + gls._acci_gross_prof_.path +\
             "' Account"
            new_tx = self.gl.create_transaction(
                tx_name,
                description=tx_name,
                tx_date=year_end_datetime,
                dt_account=dt_account,
                cr_account=cr_account,
                source=self.path,
                amount=abs(gross_profit))
            new_tx.is_closing_cr_account = True

        if gross_profit > 0:
            create_tx(gls._acci_gross_prof_.path,
                      gls._acci_inc_sum_.path)
        elif gross_profit < 0:
            create_tx(gls._acci_inc_sum_.path,
                      gls._acci_gross_prof_.path)

        return income_summary_amount

    def _perform_year_end_gross_profit(self,
                                       year_end_datetime,
                                       gross_profit_write_off_accounts):
        gls = self.gl.structure
        gross_profit = 0

        for acc, amount in gross_profit_write_off_accounts.items():
            def create_tx(dt_account, cr_account):
                tx_name = "Settle the '" + acc.path + "' Account"
                new_tx = self.gl.create_transaction(
                    tx_name,
                    description=tx_name,
                    tx_date=year_end_datetime,
                    dt_account=dt_account,
                    cr_account=cr_account,
                    source=self.path,
                    amount=abs(amount))
                new_tx.is_closing_cr_account = True

            gross_profit += amount
            if amount > 0:
                create_tx(gls._acci_gross_prof_.path, acc.path)
            elif amount < 0:
                create_tx(acc.path, gls._acci_gross_prof_.path)
        return gross_profit

    def _perform_year_end_income_summary(self,
                                         year_end_datetime,
                                         gross_profit,
                                         income_summary_write_off_accounts):
        gls = self.gl.structure
        income_summary_amount = gross_profit

        for acc, amount in income_summary_write_off_accounts.items():
            def create_tx(dt_account, cr_account):
                tx_name = "Settle the '" + acc.path + "' Account"
                new_tx = self.gl.create_transaction(
                    tx_name,
                    description=tx_name,
                    tx_date=year_end_datetime,
                    dt_account=dt_account,
                    cr_account=cr_account,
                    source=self.path,
                    amount=abs(amount))
                new_tx.is_closing_cr_account = True

            if amount > 0:
                if acc.account_type == AccountType.expense:
                    income_summary_amount -= amount
                    create_tx(gls._acci_inc_sum_.path, acc.path)
                else:
                    income_summary_amount += amount
                    create_tx(acc.path, gls._acci_inc_sum_.path)
            elif amount < 0:
                if acc.account_type == AccountType.expense:
                    income_summary_amount -= amount
                    create_tx(acc.path, gls._acci_inc_sum_.path)
                else:
                    income_summary_amount += amount
                    create_tx(gls._acci_inc_sum_.path, acc.path)
        return income_summary_amount

    def _perform_year_end_income_tax(self,
                                     year_end_datetime,
                                     income_summary_amount):
        return income_summary_amount

    def _perform_year_end_retained_earnings(self,
                                            year_end_datetime,
                                            income_summary_amount):
        def create_tx(dt_account, cr_account):
            tx_name = "Settle the '" + gls._acci_inc_sum_.path +\
                      "' Account, adjust Retained Earnings accordingly."
            new_tx = self.gl.create_transaction(
                tx_name,
                description=tx_name,
                tx_date=year_end_datetime,
                dt_account=dt_account,
                cr_account=cr_account,
                source=self.path,
                amount=income_summary_amount)
            new_tx.is_closing_cr_account = True
        gls = self.gl.structure

        if income_summary_amount > 0:
            create_tx(gls._acci_inc_sum_.path,
                      gls._accb_ret_earnings_acc_.path)
        elif income_summary_amount < 0:
            create_tx(gls._accb_ret_earnings_acc_.path,
                      gls._acci_inc_sum_.path)
예제 #14
0
class TransactionListUnitTester(unittest.TestCase):
    """
    Tester for the auxi.modelling.financial.reporting.TransactionList
    class.
    """

    def setUp(self):
        gls = Gls("NameA")
        self.gl = GeneralLedger("NameA", gls)
        self.object = TransactionList(
            data_source=self.gl,
            start=datetime(2016, 4, 1),
            end=datetime(2016, 8, 1), output_path=None)

        self.gl.create_transaction(
            "tx1",
            description='1',
            tx_date=datetime(2016, 2, 1),
            dt_account="Bank\Default1",
            cr_account="Sales\Default1",
            source="test1",
            amount=1001)
        self.gl.create_transaction(
            "tx2",
            description='2',
            tx_date=datetime(2016, 3, 1),
            dt_account="Bank\Default2",
            cr_account="Sales\Default2",
            source="test2",
            amount=1002)
        self.gl.create_transaction(
            "tx3",
            description='3',
            tx_date=datetime(2016, 4, 2),
            dt_account="Bank\Default3",
            cr_account="Sales\Default3",
            source="test3",
            amount=1003)
        self.gl.create_transaction(
            "tx4",
            description='4',
            tx_date=datetime(2016, 5, 1),
            dt_account="Bank\Default4",
            cr_account="Sales\Default4",
            source="test4",
            amount=1004)
        self.gl.create_transaction(
            "tx5",
            description='5',
            tx_date=datetime(2016, 9, 1),
            dt_account="Bank\Default5",
            cr_account="Sales\Default5",
            source="test5",
            amount=1005)

    def test__generate_table_(self):
        table = self.object._generate_table_()
        self.assertEqual(table[0], [
            "Date", "Source", "Tx Name", "Debit Account",
            "Credit Account", "Amount", "Description"])
        # There should be 4 rows, 1 for the header, 2 for transactions
        # and 1 for the 'Total' column.
        self.assertEqual(len(table), 4)
        # The first 2 and last to transactions
        # in the GL falls outside the request start and end date. Thus, only
        # the 3rd and 4th transactions should appear in the report.
        # Test that the transaction names are correct.
        self.assertEqual(table[1][2], "tx3")
        self.assertEqual(table[2][2], "tx4")
        # Test that the transaction amounts are correct.
        self.assertEqual(table[1][5], '1003.00')
        self.assertEqual(table[2][5], '1004.00')
        # Test that the transaction sources are correct.
        self.assertEqual(table[1][1], "test3")
        self.assertEqual(table[2][1], "test4")
        # Test that the transaction dates are correct.
        self.assertEqual(table[1][0], datetime(2016, 4, 2))
        self.assertEqual(table[2][0], datetime(2016, 5, 1))
        # Test that the transaction dt accounts are correct.
        self.assertEqual(table[1][3], "Bank\Default3")
        self.assertEqual(table[2][3], "Bank\Default4")
        # Test that the transaction dt accounts are correct.
        self.assertEqual(table[1][4], "Sales\Default3")
        self.assertEqual(table[2][4], "Sales\Default4")
        # Test that the transaction descriptions are correct.
        self.assertEqual(table[1][6], "3")
        self.assertEqual(table[2][6], "4")
예제 #15
0
class TransactionListUnitTester(unittest.TestCase):
    """
    Tester for the auxi.modelling.financial.reporting.TransactionList
    class.
    """

    def setUp(self):
        gls = Gls("NameA")
        self.gl = GeneralLedger("NameA", gls)
        self.object = TransactionList(
            data_source=self.gl,
            start=datetime(2016, 4, 1),
            end=datetime(2016, 8, 1), output_path=None)

        self.gl.create_transaction(
            "tx1",
            description='1',
            tx_date=datetime(2016, 2, 1),
            dt_account="Bank\Default1",
            cr_account="Sales\Default1",
            source="test1",
            amount=1001)
        self.gl.create_transaction(
            "tx2",
            description='2',
            tx_date=datetime(2016, 3, 1),
            dt_account="Bank\Default2",
            cr_account="Sales\Default2",
            source="test2",
            amount=1002)
        self.gl.create_transaction(
            "tx3",
            description='3',
            tx_date=datetime(2016, 4, 2),
            dt_account="Bank\Default3",
            cr_account="Sales\Default3",
            source="test3",
            amount=1003)
        self.gl.create_transaction(
            "tx4",
            description='4',
            tx_date=datetime(2016, 5, 1),
            dt_account="Bank\Default4",
            cr_account="Sales\Default4",
            source="test4",
            amount=1004)
        self.gl.create_transaction(
            "tx5",
            description='5',
            tx_date=datetime(2016, 9, 1),
            dt_account="Bank\Default5",
            cr_account="Sales\Default5",
            source="test5",
            amount=1005)

    def test__generate_table_(self):
        table = self.object._generate_table_()
        self.assertEqual(table[0], [
            "Date", "Source", "Tx Name", "Debit Account",
            "Credit Account", "Amount", "Description"])
        # There should be 4 rows, 1 for the header, 2 for transactions
        # and 1 for the 'Total' column.
        self.assertEqual(len(table), 4)
        # The first 2 and last to transactions
        # in the GL falls outside the request start and end date. Thus, only
        # the 3rd and 4th transactions should appear in the report.
        # Test that the transaction names are correct.
        self.assertEqual(table[1][2], "tx3")
        self.assertEqual(table[2][2], "tx4")
        # Test that the transaction amounts are correct.
        self.assertEqual(table[1][5], '1003.00')
        self.assertEqual(table[2][5], '1004.00')
        # Test that the transaction sources are correct.
        self.assertEqual(table[1][1], "test3")
        self.assertEqual(table[2][1], "test4")
        # Test that the transaction dates are correct.
        self.assertEqual(table[1][0], datetime(2016, 4, 2))
        self.assertEqual(table[2][0], datetime(2016, 5, 1))
        # Test that the transaction dt accounts are correct.
        self.assertEqual(table[1][3], "Bank\Default3")
        self.assertEqual(table[2][3], "Bank\Default4")
        # Test that the transaction dt accounts are correct.
        self.assertEqual(table[1][4], "Sales\Default3")
        self.assertEqual(table[2][4], "Sales\Default4")
        # Test that the transaction descriptions are correct.
        self.assertEqual(table[1][6], "3")
        self.assertEqual(table[2][6], "4")
예제 #16
0
class Entity(NamedObject):
    """
    Represents an entity class. An entity consists of business components
    e.g. Sales department. It executes its components and performs
    financial year end transactions.

    :param name: The name.
    :param gl_structure: The general ledger structure the entity's
      general ledger will be initialized with.
    :param description: The description.
    :param period_count: The number of periods the entity should be run for.
    """
    def __init__(self, name, gl_structure, description=None):
        self.gl = GeneralLedger("General Ledger",
                                gl_structure,
                                description="General Ledger")
        self._parent_path = ""
        self.path = name
        self.components = []
        self.negative_income_tax_total = 0
        self._prev_year_end_datetime = datetime.min
        self._curr_year_end_datetime = datetime.min
        self._exec_year_end_datetime = datetime.min
        self.period_count = -1
        super(Entity, self).__init__(name, description=description)

    def __getitem__(self, key):
        return [c for c in self.components if c.name == key][0]

    def set_parent_path(self, value):
        """
        Set the parent path and the path from the new parent path.

        :param value: The path to the object's parent
        """

        self._parent_path = value
        self.path = value + r'/' + self.name
        self._update_childrens_parent_path()

    def _update_childrens_parent_path(self):
        for c in self.components:
            c.set_parent_path(self.path)

    @property
    def name(self):
        return self._name

    @name.setter
    def name(self, value):
        self._name = value
        self.path = self._parent_path + r'/' + self.name
        self._update_childrens_parent_path()

    def create_component(self, name, description=None):
        """
        Create a component in the business entity.

        :param name: The component's name.
        :param description: The component's description.

        :returns: The created component.
        """

        new_comp = Component(name, self.gl, description=description)
        new_comp.set_parent_path(self.path)
        self.components.append(new_comp)
        return new_comp

    def remove_component(self, name):
        """
        Remove a component from the entity.

        :param name: The name of the component to remove.
        """

        component_to_remove = None
        for c in self.components:
            if c.name == name:
                component_to_remove = c
        if component_to_remove is not None:
            self.components.remove(component_to_remove)

    def prepare_to_run(self, clock, period_count):
        """
        Prepare the entity for execution.

        :param clock: The clock containing the execution start time and
          execution period information.
        :param period_count: The total amount of periods this activity will be
          requested to be run for.
        """

        self.period_count = period_count

        self._exec_year_end_datetime = clock.get_datetime_at_period_ix(
            period_count)
        self._prev_year_end_datetime = clock.start_datetime
        self._curr_year_end_datetime = clock.start_datetime + relativedelta(
            years=1)

        # Remove all the transactions
        del self.gl.transactions[:]

        for c in self.components:
            c.prepare_to_run(clock, period_count)

        self.negative_income_tax_total = 0

    def run(self, clock):
        """
        Execute the entity at the current clock cycle.

        :param clock: The clock containing the current execution time and
          period information.
        """

        if clock.timestep_ix >= self.period_count:
            return

        for c in self.components:
            c.run(clock, self.gl)

        self._perform_year_end_procedure(clock)

    def _perform_year_end_procedure(self, clock):
        if clock.get_datetime() >= self._curr_year_end_datetime\
         or clock.timestep_ix == self.period_count:
            gls = self.gl.structure
            year_start_date = self._prev_year_end_datetime
            year_end_date = self._curr_year_end_datetime + \
                timedelta(seconds=-1)

            self._prev_year_end_datetime = self._curr_year_end_datetime
            self._curr_year_end_datetime += relativedelta(years=1)
            if self._curr_year_end_datetime > self._exec_year_end_datetime:
                self._curr_year_end_datetime = self._exec_year_end_datetime

            gross_profit_write_off_accs = OrderedDict()
            inc_summary_write_off_accs = OrderedDict()
            sales_accs = gls.get_account_descendants(gls._acci_sales_)
            cost_of_sales_accs = gls.get_account_descendants(gls._acci_cos_)
            year_taxes = []
            for tx in self.gl.transactions:
                if year_start_date <= tx.tx_date <= year_end_date:
                    year_taxes.append(tx)
            for tx in year_taxes:
                cr_acc = gls.get_account(tx.cr_account)
                dt_acc = gls.get_account(tx.dt_account)

                if cr_acc in sales_accs:
                    if cr_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[cr_acc] += tx.amount
                    else:
                        gross_profit_write_off_accs[cr_acc] = tx.amount
                elif dt_acc in sales_accs:
                    if dt_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[dt_acc] -= tx.amount
                    else:
                        gross_profit_write_off_accs[dt_acc] = -tx.amount

                elif cr_acc in cost_of_sales_accs:
                    if cr_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[cr_acc] += tx.amount
                    else:
                        gross_profit_write_off_accs[cr_acc] = tx.amount
                elif dt_acc in cost_of_sales_accs:
                    if dt_acc in gross_profit_write_off_accs:
                        gross_profit_write_off_accs[dt_acc] -= tx.amount
                    else:
                        gross_profit_write_off_accs[dt_acc] = -tx.amount

                elif cr_acc.account_type == AccountType.revenue:
                    if cr_acc in inc_summary_write_off_accs:
                        inc_summary_write_off_accs[cr_acc] += tx.amount
                    else:
                        inc_summary_write_off_accs[cr_acc] = tx.amount
                elif dt_acc.account_type == AccountType.revenue:
                    if dt_acc.account_type == AccountType.revenue:
                        inc_summary_write_off_accs[dt_acc] -= tx.amount
                    else:
                        inc_summary_write_off_accs[dt_acc] = -tx.amount

                elif cr_acc.account_type == AccountType.expense:
                    if cr_acc in inc_summary_write_off_accs:
                        inc_summary_write_off_accs[cr_acc] -= tx.amount
                    else:
                        inc_summary_write_off_accs[cr_acc] = -tx.amount
                elif dt_acc.account_type == AccountType.expense:
                    if dt_acc in inc_summary_write_off_accs:
                        inc_summary_write_off_accs[dt_acc] += tx.amount
                    else:
                        inc_summary_write_off_accs[dt_acc] = tx.amount

            inc_sum = self._perform_year_end_gross_profit_and_income_summary(
                year_end_date, gross_profit_write_off_accs,
                inc_summary_write_off_accs)
            inc_sum = self._perform_year_end_income_tax(year_end_date, inc_sum)
            self._perform_year_end_retained_earnings(year_end_date, inc_sum)

    def _perform_year_end_gross_profit_and_income_summary(
            self, year_end_datetime, gross_profit_write_off_accounts,
            income_summary_write_off_accounts):
        gls = self.gl.structure
        gross_profit = self._perform_year_end_gross_profit(
            year_end_datetime, gross_profit_write_off_accounts)
        income_summary_amount = self._perform_year_end_income_summary(
            year_end_datetime, gross_profit, income_summary_write_off_accounts)

        def create_tx(dt_account, cr_account):
            tx_name = "Settle the '" + gls._acci_gross_prof_.path +\
             "' Account"
            new_tx = self.gl.create_transaction(tx_name,
                                                description=tx_name,
                                                tx_date=year_end_datetime,
                                                dt_account=dt_account,
                                                cr_account=cr_account,
                                                source=self.path,
                                                amount=abs(gross_profit))
            new_tx.is_closing_cr_account = True

        if gross_profit > 0:
            create_tx(gls._acci_gross_prof_.path, gls._acci_inc_sum_.path)
        elif gross_profit < 0:
            create_tx(gls._acci_inc_sum_.path, gls._acci_gross_prof_.path)

        return income_summary_amount

    def _perform_year_end_gross_profit(self, year_end_datetime,
                                       gross_profit_write_off_accounts):
        gls = self.gl.structure
        gross_profit = 0

        for acc, amount in gross_profit_write_off_accounts.items():

            def create_tx(dt_account, cr_account):
                tx_name = "Settle the '" + acc.path + "' Account"
                new_tx = self.gl.create_transaction(tx_name,
                                                    description=tx_name,
                                                    tx_date=year_end_datetime,
                                                    dt_account=dt_account,
                                                    cr_account=cr_account,
                                                    source=self.path,
                                                    amount=abs(amount))
                new_tx.is_closing_cr_account = True

            gross_profit += amount
            if amount > 0:
                create_tx(gls._acci_gross_prof_.path, acc.path)
            elif amount < 0:
                create_tx(acc.path, gls._acci_gross_prof_.path)
        return gross_profit

    def _perform_year_end_income_summary(self, year_end_datetime, gross_profit,
                                         income_summary_write_off_accounts):
        gls = self.gl.structure
        income_summary_amount = gross_profit

        for acc, amount in income_summary_write_off_accounts.items():

            def create_tx(dt_account, cr_account):
                tx_name = "Settle the '" + acc.path + "' Account"
                new_tx = self.gl.create_transaction(tx_name,
                                                    description=tx_name,
                                                    tx_date=year_end_datetime,
                                                    dt_account=dt_account,
                                                    cr_account=cr_account,
                                                    source=self.path,
                                                    amount=abs(amount))
                new_tx.is_closing_cr_account = True

            if amount > 0:
                if acc.account_type == AccountType.expense:
                    income_summary_amount -= amount
                    create_tx(gls._acci_inc_sum_.path, acc.path)
                else:
                    income_summary_amount += amount
                    create_tx(acc.path, gls._acci_inc_sum_.path)
            elif amount < 0:
                if acc.account_type == AccountType.expense:
                    income_summary_amount -= amount
                    create_tx(acc.path, gls._acci_inc_sum_.path)
                else:
                    income_summary_amount += amount
                    create_tx(gls._acci_inc_sum_.path, acc.path)
        return income_summary_amount

    def _perform_year_end_income_tax(self, year_end_datetime,
                                     income_summary_amount):
        return income_summary_amount

    def _perform_year_end_retained_earnings(self, year_end_datetime,
                                            income_summary_amount):
        def create_tx(dt_account, cr_account):
            tx_name = "Settle the '" + gls._acci_inc_sum_.path +\
                      "' Account, adjust Retained Earnings accordingly."
            new_tx = self.gl.create_transaction(tx_name,
                                                description=tx_name,
                                                tx_date=year_end_datetime,
                                                dt_account=dt_account,
                                                cr_account=cr_account,
                                                source=self.path,
                                                amount=income_summary_amount)
            new_tx.is_closing_cr_account = True

        gls = self.gl.structure

        if income_summary_amount > 0:
            create_tx(gls._acci_inc_sum_.path,
                      gls._accb_ret_earnings_acc_.path)
        elif income_summary_amount < 0:
            create_tx(gls._accb_ret_earnings_acc_.path,
                      gls._acci_inc_sum_.path)