Click here to Skip to main content
15,564,017 members
Please Sign up or sign in to vote.
1.00/5 (3 votes)
See more:
# Requirements: pandas, tabulate

from tabulate import tabulate
from pandas import DataFrame
from datetime import (
    datetime, timedelta)
import math


class Payable(object):

    def __init__(self, original_payment_date, amount, fee, installment, recipient_id):
        self.original_payment_date = original_payment_date
        self.payment_date = original_payment_date
        self.amount = amount
        self.fee = fee
        self.installment = installment
        self.recipient_id = recipient_id
        self.anticipation_fee = 0
        self.anticipated = False
        self.duration = 0
        self.rebalance()

    def anticipate(self, anticipation_fee, anticipation_date):
        """
        Calculate anticipation fee when anticipating
        Can't anticipate if anticipation_date > original_payment_date
        """

        if anticipation_date >= self.original_payment_date:
            return

        self.duration = (self.original_payment_date - anticipation_date).days
        self.anticipation_fee = round(
            (self.duration/30) * (anticipation_fee/100) * (self.amount - self.fee))
        self.payment_date = anticipation_date
        self.anticipated = True
        self.rebalance()

    def rebalance(self):
        """
        Calculate payable net amount
        """
        self.net_amount = self.amount - self.fee - self.anticipation_fee

    def describe(self):
        return dict(
            original_payment_date=self.original_payment_date,
            payment_date=self.payment_date,
            amount=self.amount,
            fee=self.fee,
            installment=self.installment,
            recipient_id=self.recipient_id,
            anticipation_fee=self.anticipation_fee,
            anticipated=self.anticipated,
            duration=self.duration,
            net_amount=self.net_amount)


class Transaction(object):

    def __init__(self, split_rules, date, amount, installments, mdr):
        self.split_rules = split_rules
        self.date = date
        self.amount = amount
        self.installments = installments
        self.mdr = mdr

        self.calculate_fee()
        self.calculate_recipients_amounts()
        self.calculate_payables()

    def calculate_fee(self):
        """
        Calculate transaction fee
        """
        self.fee = round(self.amount * self.mdr / 100)

    def calculate_recipients_amounts(self):
        """
        Calculate amount and fee to be received by each recipient
        Remainder values are added to charge remainder
        """
        self.recipients_amounts = {}

        amount_remainder = self.amount
        fee_remainder = self.fee

        for recipient in self.split_rules.recipients:

            recipient_id = recipient.recipient_id
            self.recipients_amounts[recipient_id] = {}

            recipient_amount = round(
                self.amount * recipient.percentage / self.split_rules.cumulated_percentage)
            self.recipients_amounts[recipient_id]["amount"] = recipient_amount
            amount_remainder -= recipient_amount

            if recipient.charge_processing_fee is True:
                recipient_fee = round(
                    self.fee * recipient.percentage / self.split_rules.fee_cumulated_percentage)
            else:
                recipient_fee = 0

            self.recipients_amounts[recipient_id]["fee"] = recipient_fee
            fee_remainder -= recipient_fee

        charge_remainder_recipient = self.split_rules.get_charge_remainder()
        self.recipients_amounts[charge_remainder_recipient][
            "amount"] += amount_remainder
        self.recipients_amounts[charge_remainder_recipient][
            "fee"] += fee_remainder

    def calculate_payables(self):
        """
        Create a payable for each recipient and each installment
        """
        self.payables = []

        payment_dates = calculate_dates(self.date, self.installments)

        for recipient_id, recipient_amounts in self.recipients_amounts.items():

            amount = recipient_amounts["amount"]
            fee = recipient_amounts["fee"]

            amount_divisions = calculate_installments(
                amount, self.installments)
            fee_divisions = calculate_installments(fee, self.installments)

            for installment in range(1, self.installments + 1):

                installment_payment_date = payment_dates[installment]
                installment_amount = amount_divisions[installment]
                installment_fee = fee_divisions[installment]

                self.payables.append(
                    Payable(installment_payment_date, installment_amount, installment_fee, installment, recipient_id))


class Recipient(object):

    def __init__(self, recipient_id, percentage, charge_processing_fee, charge_remainder):
        self.recipient_id = recipient_id
        self.percentage = percentage
        self.charge_processing_fee = charge_processing_fee
        self.charge_remainder = charge_remainder


class SplitRules(object):

    def __init__(self):
        self.recipients = []
        self.cumulated_percentage = None
        self.fee_cumulated_percentage = None

    def add_recipient(self, recipient_id, percentage, charge_processing_fee, charge_remainder):
        """
        Add recipient to split rule
        """
        self.recipients.append(
            Recipient(recipient_id, percentage, charge_processing_fee, charge_remainder))
        self.rebalance()

    def rebalance(self):
        """
        Save cumulated percentage and fee cumulated percentage
        to normalize distribution
        """
        self.cumulated_percentage = sum(
            [recipient.percentage for recipient in self.recipients])
        self.fee_cumulated_percentage = sum(
            [recipient.percentage for recipient in self.recipients if recipient.charge_processing_fee is True])

    def get_charge_remainder(self):
        return (next(filter(lambda recipient: recipient.charge_remainder is True, self.recipients))).recipient_id


class Company(object):

    def __init__(self, anticipation_fee, mdrs):
        self.anticipation_fee = anticipation_fee
        self.mdrs = mdrs

    def get_mdr(self, installments):
        """
        Return the mdr to be applied given the number of installments
        """
        if installments == 1:
            return self.mdrs[1]
        elif installments in range(2, 7):
            return self.mdrs[2]
        else:
            return self.mdrs[7]

    def create_transaction(self, split_rules, date, amount, installments):
        mdr = self.get_mdr(installments)
        return Transaction(split_rules, date, amount, installments, mdr)

    def anticipate_payables(self, anticipation_date, payables):
        for payable in payables:
            payable.anticipate(self.anticipation_fee, anticipation_date)


def calculate_installments(amount, installments):
    """
    Given an amount and a number of installments,
    returns the amount distribution on each installment
    """
    amounts = {}
    base_amount = math.floor(amount / installments)
    remainder = (amount % installments)

    if remainder <= 1:
        for installment in range(1, installments + 1):
            amounts[installment] = base_amount + \
                (remainder if installment == 1 else 0)
        return amounts

    missing_cents = remainder % (installments - 1)

    if (missing_cents < (installments - 1)):
        missing_cents = installments - 1 - missing_cents

    other_installments = (missing_cents + remainder) / (installments - 1)

    for installment in range(1, installments + 1):
        amounts[installment] = int(
            base_amount + (-1 * missing_cents if installment == 1 else other_installments))

    return amounts


def calculate_dates(initial_date, installments):
    """
    Calculate payment dates given an initial date and the
    number of installments  
    """
    dates = {}
    for offset, installment in enumerate(range(1, installments + 1)):
        base_date = initial_date + timedelta(offset * 30 + 29)
        final_date = add_business_days(base_date, 2)
        dates[installment] = final_date
    return dates


def is_holiday(date):
    """
    Checks if a given date is holiday in Brazil
    """
    if not isinstance(date, str):
        date = date.strftime("%Y-%m-%d")

    holidays = [
        "2017-01-01", "2017-02-27", "2017-02-28", "2017-04-14", "2017-04-21", "2017-05-01", "2017-06-15",
        "2017-07-09", "2017-09-07", "2017-10-12", "2017-11-02", "2017-11-15", "2017-11-20", "2017-12-25",
        "2017-12-29", "2018-01-01", "2018-01-25", "2018-02-12", "2018-02-13", "2018-03-30", "2018-04-21",
        "2018-05-01", "2018-05-31", "2018-07-09", "2018-09-07", "2018-10-12", "2018-11-02", "2018-11-15",
        "2018-11-20", "2018-12-25", "2018-12-31", "2019-01-01", "2019-01-25", "2019-03-04", "2019-03-05",
        "2019-04-19", "2019-04-21", "2019-05-01", "2019-06-20", "2019-07-09", "2019-09-07", "2019-10-12",
        "2019-11-02", "2019-11-15", "2019-11-20", "2019-12-25", "2019-12-31", "2020-01-01", "2020-01-25",
        "2020-02-24", "2020-02-25", "2020-04-10", "2020-04-21", "2020-05-01", "2020-06-11", "2020-07-09",
        "2020-09-07", "2020-10-12", "2020-11-02", "2020-11-15", "2020-11-20", "2020-12-25", "2020-12-31"]

    return (date in holidays)


def add_business_days(date, days):
    """
    Returns date after a given number of business days
    """
    count = 0
    while count < days:
        date += timedelta(1)
        if (date.weekday() not in [5, 6]) and (not is_holiday(date)):
            count += 1
    return date


def process_dataframe(title, dataframe, print_report, save_report):
    table = tabulate(dataframe, headers='keys', tablefmt='psql')

    if print_report is True:
        print("\n     **  {}  **\n".format(title))
        print(table)

    if save_report is True:
        with open("transaction_report.txt", "a") as report_file:
            report_file.write("\n     **  {}  **\n".format(title))
            report_file.write(table + "\n")


def report(transaction, print_report=True, save_report=False):
    payables = transaction.payables

    recipients = {}
    for payable in payables:
        recipient_id = payable.recipient_id
        if recipient_id in recipients:
            recipients[recipient_id].append(payable)
        else:
            recipients[recipient_id] = [payable]

    total_df = DataFrame()

    for recipient_id in sorted(recipients.keys()):
        recipient_payables = recipients[recipient_id]

        df = DataFrame([payable.describe() for payable in recipient_payables])
        df = df.set_index('installment')
        df = df[["amount", "fee", "anticipation_fee", "net_amount",
                 "duration", "payment_date", "original_payment_date"]]
        process_dataframe(recipient_id, df, print_report, save_report)

        sum_row = {col: df[col].sum() for col in [
            "amount", "fee", "anticipation_fee", "net_amount"]}
        sum_df = DataFrame(sum_row, index=[recipient_id])
        total_df = total_df.append(sum_df)

    sum_row = {col: total_df[col].sum() for col in [
        "amount", "fee", "anticipation_fee", "net_amount"]}
    sum_df = DataFrame(sum_row, index=["Total"])
    total_df = total_df.append(sum_df)
    total_df = total_df[["amount", "fee", "anticipation_fee", "net_amount"]]
    process_dataframe("Total", total_df, print_report, save_report)


def main():
    """
    Create company, set anticipation and mdr fees
    """
    company = Company(
        anticipation_fee=2.5,
        mdrs={
            1: 2.25,  # for 1 installment
            2: 2.50,  # between 2-6 installments
            7: 2.88   # 7+ installments
        })

    """ 
    Create Split Rule and add multiple recipients configurations
    Cumulated percentage is not necessarily 100 once it will be normalized
    At least one recipient must have charge_processing_fee = True
    Only one recipient can have charge_remainder = True
    """
    split_rules = SplitRules()

    split_rules.add_recipient(
        recipient_id="Recipient_1",
        percentage=20,
        charge_processing_fee=True,
        charge_remainder=False)

    split_rules.add_recipient(
        recipient_id="Recipient_2",
        percentage=30,
        charge_processing_fee=True,
        charge_remainder=False)

    split_rules.add_recipient(
        recipient_id="Recipient_3",
        percentage=40,
        charge_processing_fee=False,
        charge_remainder=False)

    split_rules.add_recipient(
        recipient_id="Recipient_4",
        percentage=60,
        charge_processing_fee=True,
        charge_remainder=True)

    """
    Create a transaction on current date
    using company and split rules
    """
    current_date = datetime.now().date()

    transaction = company.create_transaction(
        split_rules=split_rules,
        date=current_date,
        amount=309698,
        installments=11)

    """
    Simulate anticipation for Recipient_2 to
    current date + 29 days + 1 business day
    """
    recipient2_payables = list(filter(
        lambda payable: payable.recipient_id == "Recipient_2",
        transaction.payables))

    anticipation_date = add_business_days(current_date + timedelta(29), 1)

    company.anticipate_payables(anticipation_date, recipient2_payables)

    report(transaction, print_report=True, save_report=True)

if __name__ == "__main__":
    main()


What I have tried:

i dont know python so i am unable to understand this code for my assignment
Posted
Updated 4-Dec-21 4:49am

Quote:
i dont know python so i am unable to understand this code for my assignment
Then either learn Python, write your own C++ code, or fail your course. Your choice.

This is not a code conversion service: we are not here to translate code for you.
Even if we did, what you would end up with would not be "good code" in the target language – they are based on very different frameworks, and what makes something work in one language does not always "translate" directly into another.
So what you end up with is very poor code, that is difficult if not impossible to maintain, that can’t be upgraded nicely, and that will cause you immense headaches if the original is changed. And it'll be a nightmare to debug if it doesn’t work "straight out of the box".
Instead, use the source code as a specification for a new app written in and for the target language / framework and write it from scratch using the original as a "template". You will get a much, much better result that will save you a lot of time in the long run.
 
Share this answer
 
Yeah, the only person that's going to convert that code is YOU.

The problem with "converting" code for a school assignment is you actually triple the amount of work you actually have to do.

You now have to learn Python. That's an entire course in itself.

You have to pick apart the code you want to convert so you can understand what it's doing and why.

Then you have to write new code to do the same thing in C++.

But, if you just understood the assignment problem and C++ enough, you could just skip the entire conversion process and write the code from scratch. It would save you a lot of time.
 
Share this answer
 
Try this:
C++
/*
 ******************************************************************************
 *
 *    Name      : Example.cpp
 *
 ******************************************************************************
 */

#include <stdio.h>
#include <stdlib.h>

static unsigned long long theKeys[] =
{
	9097624470788603392LL, 4485938452361199104LL, 0, 0,
	4693335872299862016LL, 4485938452361199104LL, 4702111234474982912LL, 9097624732831990016LL,
	4485938452361199104LL, 4702111234610512128LL, 4711136060342747392LL, 0,
	4702111234610512128LL, 4485938452361199104LL, 9097624732831990016LL, 4702396244260176128LL,
	0xFF
};

int main(
	int		argc,
	char** argv
)
{
	printf("\n\n\n\n");
	for (int key = 0; theKeys[key] != 0xFF; key += 4)
	{
		for (int row = 0; row < 7; ++row)	// for each row in the maps
		{
			printf("      ");
			for (int i = 0; i < 4; ++i)
			{
				if (theKeys[key + i] == 0)
					break;
				unsigned long long lnext = theKeys[key + i] >> (56 - (row * 8));	// get the map entry
				unsigned char next = lnext & 0xFF;
				for (int col = 7; col >= 0; --col)
				{
					printf("%c", (next & (1 << col)) ? '#' : ' ');
				}
				printf("  ");	// at the end of the columns add two spaces
			}
			printf("\n");
		}
		printf("\n\n");
	}

	return 0;
}
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900