示例#1
0
    def build_row(self, buyer, price, grades, sale_type, adjustment, volume):
        if adjustment is None:
            adjustment = CV(self.report.season.default_adjustment, self.report.season.exchange_rate)
        else:
            adjustment = CV(adjustment, self.report.season.exchange_rate)

        if sale_type == 'FOT':
            fot_price = price
            if fot_price.as_local(self.report.season.exchange_rate) > Decimal(0):
                fob_price = fot_price + adjustment
            else:
                fob_price = CV_ZERO
            freight = adjustment * volume
            revenue = fob_price * volume

        elif sale_type == 'FOB':
            fob_price = price
            fot_price = fob_price - adjustment
            if fot_price.as_local(self.report.season.exchange_rate) < Decimal(0):
                fot_price = CV_ZERO
            freight = CV_ZERO
            revenue = fob_price * volume

        elif sale_type == 'LOC':
            fob_price = CV_ZERO
            fot_price = CV_ZERO
            price = price
            adjustment = CV_ZERO
            freight = CV_ZERO
            revenue = price * volume
        else: # pragma: no cover
            raise Exception("Invalid sale type: '%s' for sale to '%s" % (sale_type, buyer))

        return SalesRow(buyer, grades, sale_type, volume, price, fot_price, fob_price, freight, revenue)        
示例#2
0
    def test_cloning(self):
        val = CV(Decimal("1000"))
        val2 = CV(val)
        self.assertEquals(val.as_local(Decimal("500")),
                          val2.as_local(Decimal("500")))

        try:
            val2 = CV(val, Decimal("500"))
            self.assertFalse(True, "Should have thrown, invalid constructor")
        except:
            pass
示例#3
0
    def build_expense_row(self, entries, index):
        row = entries[index]
        children = []

        for i in range(index+1, len(entries)):
            entry = entries[i]

            if entry['expense'].depth <= 1:
                break

            children.append(self.build_row(entry['expense'], CV(entry['value'], entry['exchange_rate'])))

        return self.build_row(row['expense'], CV(row['value'], row['exchange_rate']), children)
示例#4
0
    def test_conversion(self):
        val = CV(Decimal("1000"))

        self.assertEquals(Decimal("1000"), val.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), val.as_usd(Decimal("500")))

        self.assertEquals("1000 - []", str(val))

        val = CV(Decimal("2"), Decimal("500"))
        self.assertEquals(Decimal("1000"), val.as_local(Decimal("500")))
        self.assertEquals(Decimal("1200"), val.as_local(Decimal("600")))
        self.assertEquals(Decimal("2"), val.as_usd(Decimal("500")))

        self.assertEquals("None - [2 (500)]", str(val))
示例#5
0
    def test_division(self):
        val = CV(None)
        total = val / None

        self.assertIsNone(total)
        self.assertIsNone(total)

        total = None / val

        self.assertIsNone(total)
        self.assertIsNone(total)

        val = CV(Decimal("1000"))
        total = None / val

        self.assertIsNone(total)
        self.assertIsNone(total)

        total = val / None

        self.assertIsNone(total)
        self.assertIsNone(total)

        total = val / Decimal("2")

        self.assertEquals(Decimal("500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("1"), total.as_usd(Decimal("500")))

        total = val
        total /= Decimal("2")

        self.assertEquals(Decimal("500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("1"), total.as_usd(Decimal("500")))

        try:
            total = Decimal("2") / val
            self.assertTrue(
                False,
                "Should have raised exception as numerator must always be CurrencyValue"
            )
        except Exception as e:
            pass

        val = CV(Decimal("4"), Decimal("500")) + CV(Decimal("1000"))
        total = val / Decimal("-2")

        self.assertEquals(Decimal("-1500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-3"), total.as_usd(Decimal("500")))
示例#6
0
    def populate_calculated(self):
        # first calculate all our totals
        self.working_cap_utilized = CV_ZERO
        categories = self.expenses.get_categories()
        if categories:
            self.working_cap_utilized += categories[0].value

        self.working_cap_received = CV_ZERO
        if self.report.working_capital:
            self.working_cap_received = CV(self.report.working_capital)

        self.working_cap_repaid = CV_ZERO
        if self.report.working_capital_repaid:
            self.working_cap_repaid = CV(self.report.working_capital_repaid)

        cash_due = self.expenses.total_revenue

        # subtract all our category totals
        for category in categories[1:]:
            cash_due -= category.value

        # and our forex loss
        cash_due -= self.expenses.total_forex_loss

        # and our working capital received
        cash_due -= self.working_cap_received
        cash_due += self.working_cap_repaid
        
        self.cash_due = cash_due

        self.unused_working_capital = self.working_cap_received - self.working_cap_utilized - self.working_cap_repaid

        # calculate retained profit
        self.retained_profit = self.expenses.total_profit
        for use in self.uses:
            if use.total:
                self.retained_profit -= use.total

        if self.retained_profit.as_local(self.exchange) < 0:
            self.retained_profit = CV(0)

        # then set them on our sources of cash if appropriate
        self.set_calculated_source_value('WCAP', self.unused_working_capital)
        self.set_calculated_source_value('CDUE', self.cash_due)

        self.set_calculated_use_value('PROF', self.retained_profit)
示例#7
0
    def test_nones(self):
        val = CV(None)
        self.assertIsNone(val.as_local(Decimal("585")))
        self.assertIsNone(val.as_usd(Decimal("585")))

        val = CV(None, Decimal("585"))
        self.assertIsNone(val.as_local(Decimal("585")))
        self.assertIsNone(val.as_usd(Decimal("585")))

        try:
            val = CV(Decimal("5"))
            val.as_usd(None)
            self.assertFalse(
                True,
                "Should have raised exception due to missing exchange rate")
        except:
            pass
示例#8
0
    def build_sources(self):
        self.sources = []
        for source in self.report.cash_sources_for_season():
            total = None
            
            if source.entry:
                total = CV(source.entry.value)

            self.sources.append(SourceRow(source, total))
示例#9
0
    def build_uses(self):
        self.uses = []
        for cashuse in self.report.cash_uses_for_season():
            total = None

            if cashuse.entry:
                total = CV(cashuse.entry.value)

            self.uses.append(UseRow(cashuse, total))
示例#10
0
    def __init__(self, report, production_box, sales_box, currency):
        self.report = report
        self.production_box = production_box
        self.sales_box = sales_box
        self.currency = currency
        self.exchange = self.report.season.exchange_rate

        self.sales_revenue = sales_box.total_revenue
        self.total_revenue = sales_box.total_revenue
        self.misc_revenue = CV(None)

        # if we have misc revenue (TZ in some cases) then our total must take that into account
        if self.report.season.has_misc_revenue:
            self.misc_revenue = CV(self.report.miscellaneous_revenue)
            self.total_revenue += self.misc_revenue

        self.categories = self.build_category_rows()

        self.calculate_totals()
示例#11
0
    def calculate_totals(self):
        self.total = CV(Decimal("0"))
        self.total_advance = CV(Decimal("0"))
        self.total_expense = CV(Decimal("0"))

        # calculate our overall advance
        for category in self.categories:
            self.total += category.value
            self.total_advance += category.advance_value

        # total forex loss
        self.total_forex_loss = self.total_revenue.forex_loss(self.exchange) - self.total.forex_loss(self.exchange)

        # total profit
        self.total_profit = self.total_revenue - self.total - self.total_forex_loss

        # total expenses per kilo green
        total_expense = self.total - self.total_advance
        if self.production_box.green_total:
            self.production_cost = total_expense / self.production_box.green_total
        else:
            self.production_cost = CV(Decimal("0"))
示例#12
0
    def build_total_row(self, rows):
        total_volume = Decimal("0")
        total_revenue = CV(Decimal("0"))
        total_freight = CV(Decimal("0"))

        total_fob = CV(Decimal("0"))
        total_fot = CV(Decimal("0"))

        for row in rows:
            total_volume += row.volume
            total_revenue += row.revenue
            total_freight += row.freight

            total_fob += (row.volume * row.fob_price)
            total_fot += (row.volume * row.fot_price)

        fot_price = total_fot / total_volume
        fob_price = total_fob / total_volume
        price = total_revenue / total_volume

        return SalesRow("Total Sales", [], "", total_volume, price, fot_price, fob_price, 
                        total_freight, total_revenue, is_total=True)
示例#13
0
    def test_multiplication(self):
        val = CV(None)
        total = val * None

        self.assertIsNone(total)
        self.assertIsNone(total)

        total = None * val

        self.assertIsNone(total)
        self.assertIsNone(total)

        val = CV(Decimal("1000"))
        total = None * val

        self.assertIsNone(total)
        self.assertIsNone(total)

        total = val * None

        self.assertIsNone(total)
        self.assertIsNone(total)

        total = val * Decimal("2")

        self.assertEquals(Decimal("2000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("4"), total.as_usd(Decimal("500")))

        total = Decimal("2") * val

        self.assertEquals(Decimal("2000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("4"), total.as_usd(Decimal("500")))

        total = val
        total *= Decimal("2")

        self.assertEquals(Decimal("2000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("4"), total.as_usd(Decimal("500")))

        val = CV(Decimal("4"), Decimal("500")) + CV(Decimal("1000"))
        total = Decimal("-2") * val

        self.assertEquals(Decimal("-6000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-12"), total.as_usd(Decimal("500")))

        # test invalid combinations
        val1 = CV(Decimal("10"))
        val2 = CV(Decimal("20"))

        try:
            total = val1 * val2
            self.assertFalse(True, "Should have thrown, invalid operation")
        except:
            pass
示例#14
0
    def build_category_row(self, entries, index):
        category = entries[index]
        children = []

        for i in range(index+1, len(entries)):
            entry = entries[i]
            
            # new category?  all done
            if entry['expense'].depth == 0:
                break

            # depth 1?  roll it up
            if entry['expense'].depth == 1:
                row = self.build_expense_row(entries, i)
                children.append(row)

        return self.build_row(category['expense'], CV(category['value'], category['exchange_rate']), children)
示例#15
0
    def test_addition(self):
        val = CV(None)
        val2 = CV(None)
        total = val + val2

        self.assertIsNone(total.as_local(Decimal("585")))
        self.assertIsNone(total.as_usd(Decimal("585")))

        val = CV(Decimal("1000"))

        total = val + val2

        self.assertEquals(Decimal("1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), total.as_usd(Decimal("500")))

        total = val2 + val

        self.assertEquals(Decimal("1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), total.as_usd(Decimal("500")))

        total = None + val

        self.assertEquals(Decimal("1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), total.as_usd(Decimal("500")))

        total = val + None

        self.assertEquals(Decimal("1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), total.as_usd(Decimal("500")))

        val = CV(Decimal("1"), Decimal("500"))
        val2 = CV(Decimal("2"), Decimal("250"))

        total = val + val2

        self.assertEquals(Decimal("1500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("3"), total.as_usd(Decimal("500")))

        total += CV(Decimal("1000"))

        self.assertEquals(Decimal("2500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("5"), total.as_usd(Decimal("500")))

        total = total.negate()

        self.assertEquals(Decimal("-2500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-5"), total.as_usd(Decimal("500")))
示例#16
0
    def test_subtraction(self):
        val = CV(None)
        val2 = CV(None)
        total = val - val2

        self.assertIsNone(total.as_local(Decimal("500")))
        self.assertIsNone(total.as_usd(Decimal("500")))

        val = CV(Decimal("1000"))

        total = val - val2

        self.assertEquals(Decimal("1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), total.as_usd(Decimal("500")))

        total = val2 - val

        self.assertEquals(Decimal("-1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-2"), total.as_usd(Decimal("500")))

        total = None - val

        self.assertEquals(Decimal("-1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-2"), total.as_usd(Decimal("500")))

        total = val - None

        self.assertEquals(Decimal("1000"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("2"), total.as_usd(Decimal("500")))

        val = CV(Decimal("1"), Decimal("500"))
        val2 = CV(Decimal("2"), Decimal("250"))

        total = val - val2

        self.assertEquals(Decimal("-500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-1"), total.as_usd(Decimal("500")))

        total -= CV(Decimal("1000"))

        self.assertEquals(Decimal("-1500"), total.as_local(Decimal("500")))
        self.assertEquals(Decimal("-3"), total.as_usd(Decimal("500")))
示例#17
0
    def build_rows(self, sales):
        rows = []
        combination_sales = []

        # for each sale
        for sale in sales:
            volume = Decimal(0)
            grades = []

            # whether all the grades in this sale are depth 2.. if so, then this sale
            # will be combined with other sales that are all depth2 if by the same buyer
            can_be_combined = sale.components.count() > 0
            depth1_parent = -1

            for component in sale.components.all():
                grade = component.grade
                if not grade.parent or not grade.parent.parent:
                    can_be_combined = False
                else:
                    grade_depth1_parent = self.get_depth1_parent(grade)
                    if depth1_parent == -1:
                        depth1_parent = grade_depth1_parent

                    if depth1_parent != grade_depth1_parent:
                        can_be_combined = False

                volume += component.volume
                if not component.grade in grades:
                    grades.append(component.grade)
            

            row = self.build_row(sale.buyer, CV(sale.price, sale.exchange_rate), 
                                 grades, sale.sale_type, sale.adjustment, volume)

            if can_be_combined:
                combination_sales.append(row)
            else:
                rows.append(row)

        # now we try to combine all the depth2 sales
        depth2_buyers = dict()
        for sale in combination_sales:
            sale_key = "%s_%s_%s" % (sale.buyer, sale.sale_type, sale.get_depth1_grade().id)
            
            if sale_key in depth2_buyers:
                depth2_buyers[sale_key].append(sale)
            else:
                depth2_buyers[sale_key] = [sale]

        for sales in depth2_buyers.values():
            total_volume = Decimal(0)
            total_fot_revenue = CV_ZERO
            total_fob_revenue = CV_ZERO
            total_freight = CV_ZERO
            depth1_grade = None

            fot_price = CV_ZERO
            fob_price = CV_ZERO

            for sale in sales:
                total_volume += sale.volume
                total_fot_revenue += (sale.volume * sale.fot_price)
                total_fob_revenue += (sale.volume * sale.fob_price)
                total_freight += sale.freight

                if depth1_grade is None:
                    for grade in sale.grades:
                        depth1_grade = self.get_depth1_parent(grade)
                        break

            if total_volume > Decimal("0"):
                fot_price = total_fot_revenue / total_volume
                fob_price = total_fob_revenue / total_volume

            # we don't call build_row here because build_row side-effects our total forex loss and
            # those have already been accounted for above
            combined_row = SalesRow(sales[0].buyer, [depth1_grade], sales[0].sale_type, 
                                    total_volume, fob_price, fot_price, fob_price, total_freight, total_fob_revenue)

            rows.append(combined_row)

        return rows
示例#18
0
 def test_forex_loss(self):
     val = CV(Decimal("50"), Decimal("500"))
     self.assertEquals(
         Decimal("500"),
         val.forex_loss(Decimal("510")).as_local(Decimal("500")))
示例#19
0
    def build_rows(self):
        self.rows = []

        # build our advance payment
        self.rows.append(
            FarmerRow(_("Advance Payment"), 'advance_payment', 'ALL',
                      self.expenses.total_advance))

        for payment in self.report.farmer_payments_for_season():
            total = None
            entry = payment.entry
            row_for = None

            if payment.applies_to == 'ALL':
                row_for = 'ALL'
                if entry:
                    total = CV(self.times_cherry(entry.all_per_kilo))
                self.rows.append(
                    FarmerRow(payment.name, 'fp__%d' % payment.id, row_for,
                              total))

            if payment.applies_to == 'BOT' or payment.applies_to == 'MEM':
                row_for = 'MEM'
                if entry:
                    total = CV(self.times_cherry(entry.member_per_kilo))
                self.rows.append(
                    FarmerRow(payment.name, 'fp__%d' % payment.id, row_for,
                              total))

            if payment.applies_to == 'BOT' or payment.applies_to == 'NON':
                row_for = 'NON'
                if entry is None:
                    total = None
                else:
                    total = CV(self.times_cherry(entry.non_member_per_kilo))
                self.rows.append(
                    FarmerRow(payment.name, 'fp__%d' % payment.id, row_for,
                              total))

        # calculate our totals
        label = _("Total Farmer Payment")

        # no members for this season means just having a simple total
        if not self.report.season.has_members:
            self.rows.append(
                self.build_total_row(self.rows, label, 'total', 'ALL'))

            self.total_paid = self.rows[-1].value
            self.total_paid_members = None
            self.total_paid_non_members = None

            self.price = self.per_weight_unit_green(self.rows[-1].value)
            self.member_price = None
            self.non_member_price = None

        else:
            self.price = None

            self.rows.append(
                self.build_total_row([
                    row for row in self.rows if row.row_for in ('ALL', 'MEM')
                ], label, 'total', 'MEM'))
            self.member_price = self.per_weight_unit_green(self.rows[-1].value)
            self.total_paid_members = self.rows[-1].value
            self.total_paid = self.rows[-1].value

            self.rows.append(
                self.build_total_row([
                    row
                    for row in self.rows[:-1] if row.row_for in ('ALL', 'NON')
                ], label, 'total', 'NON'))
            self.non_member_price = self.per_weight_unit_green(
                self.rows[-1].value)
            self.total_paid_non_members = self.rows[-1].value