예제 #1
0
    def test_serializes_correctly(self):
        """
        Test that only specific fields are serialized
        """

        spent_by = Person(username="******")
        spent_for = Person(username="******")
        budget_group = BudgetGroup()
        spent_at = datetime(2017, 05, 17)

        expense = Expense(spent_by=spent_by,
                          spent_for=spent_for,
                          budget_group=budget_group,
                          amount_spent=100,
                          spent_at=spent_at)

        serializer = ExpenseSerializer(expense)
        self.assertEqual(
            {
                'id': str(expense.id),
                'spent_at': '2017-05-17T00:00:00',
                'spent_for': spent_for.id,
                'spent_for_purpose': u'',
                'spent_by': spent_by.id,
                'amount_spent': u'100.00'
            }, serializer.data)
예제 #2
0
def new_expense(params, user, group):
    """
    Check if params are valid and create a new expense.

    Returns a text to send to the user.
    """
    if not params:
        return 'Necesito que me digas cuanto pagaste y una descripción del gasto.'

    amount_received, *description = params

    try:
        amount = amount_received.replace(',', '.')
        amount = float(amount)

    except ValueError:
        return 'El primer valor que me pasas después del comando tiene que ser el valor de lo '\
               'que pagaste, "{}" no es un número válido.'.format(amount_received)

    if not description:
        return 'Necesito que agregues una descripción del gasto.'

    description = ' '.join(description)
    expense = Expense(user=user, group=group, description=description, amount=amount)
    expense.save()

    return 'Se guardo tu gasto {}'.format(expense)
예제 #3
0
    def create(self, request, **kwargs):
        parts_to_be_saved = []
        try:
            json_args = request.data

            exp = Expense(
                owner=Profile.objects.get(user=request.user),
                description=json_args['description'],
                expense_date=datetime.strptime(json_args['expense_date'][:10], "%Y-%m-%d").date()
            )

            for part in json_args['expense_parts']:
                p = ExpensePart(
                    expense=exp,
                    budget_line_id=part['budget_line_id'],
                    amount=part['amount']
                )
                parts_to_be_saved.append(p)
        except KeyError:
            return Response(status=status.HTTP_400_BAD_REQUEST)
        exp.save()
        for p in parts_to_be_saved:
            p.expense = exp
            p.save()

        exp_dict = {'expense': exp.to_dict()}
        return Response(exp_dict, status=status.HTTP_201_CREATED)
예제 #4
0
 def test_expense_too_small(self):
     o = Expense(
         title="Bazooka",
         amount=0.02,
         date=f"2020-02-23",
     )
     self.assertRaises(ValidationError, o.full_clean)
예제 #5
0
def create(request):
    post_data = request.POST
    if not post_data:
        return redirect(new)

    expense = Expense(name=post_data['name'],
                      source=post_data.get('source', None),
                      amount=post_data['amount'],
                      date=post_data['date'])
    try:
        expense.full_clean()
    except ValidationError as e:

        for message in e.messages:
            messages.error(request, message)

        context = RequestContext(
            request, {
                'expense': expense,
                'F_checked': 'checked' if expense.source == "F" else None,
                'C_checked': 'checked' if expense.source == "C" else None,
                'H_checked': 'checked' if expense.source == "H" else None
            })
        return render(request, 'expenses/new.html', context)

    else:
        expense.save()
        return redirect(index)
예제 #6
0
 def test_expense_too_big(self):
     o = Expense(
         title="Boeing 747",
         amount=212_000_000,
         date=f"2020-02-23",
     )
     self.assertRaises(ValidationError, o.full_clean)
예제 #7
0
def new_expense(params, user, group):
    """
    Check if params are valid and create a new expense.

    Returns a text to send to the user.
    """
    try:
        data = decode_expense_params(params, group)
    except ParameterError as e:
        return str(e)

    response_text = ''
    amount = data['amount']
    description = data['description']
    date = data['dd']
    tags = data['tt']
    expense = Expense(user=user, group=group, description=description, amount=amount, date=date)
    if data['exchange_rate']:
        exchange_rate = data['exchange_rate']
        expense.original_currency = exchange_rate.currency
        expense.original_amount = data['original_amount']
        response_text += 'Tu gasto se convirtió a {} usando un tipo de cambio = ${} (cargado el ' \
            '{}).\n\n'.format(
                CURRENCY[exchange_rate.currency],
                exchange_rate.rate,
                exchange_rate.date
            )
    expense.save()
    if tags:
        for tag in tags:
            expense.tags.add(tag)

    response_text += 'Se guardó tu gasto {}'.format(expense)
    return response_text
예제 #8
0
def _make_expense_from_line(line):
    """
    Return an :class:`expenses.models.Expense` built from the supplied `line`.

    Note that the elements found in `line` should be strings. This function
    takes care of converting them to the expected db types.

    :param list line: A list of columns containing values to be put into a
        :class:`expenses.models.Expense` instance.
    :raise BadCSVFile: If the elements in the `line` can not be properly
        converted.
    :return: Non-persisted :class:`expenses.models.Expense` instance.
    :rtype: :class:`expenses.models.Expense`
    """
    try:
        return Expense(date=datetime.strptime(line[0], '%m/%d/%Y'),
                       category=line[1],
                       employee_name=line[2],
                       employee_address=line[3],
                       expense_description=line[4],
                       pre_tax_amount=line[5].replace(',', ''),
                       tax_name=line[6],
                       tax_amount=line[7].replace(',', ''))
    except ValueError:
        raise BadCSVFile
예제 #9
0
    def execute(self) -> Expense:
        expense = Expense(title=self.title,
                          amount=self.amount,
                          created_at=self.created_at,
                          tags=self.tags,
                          email=self.email).AddExpense()

        return expense
예제 #10
0
def template_run(request, pk):
    template = get_object_or_404(ExpenseTemplate, pk=pk, user=request.user)
    expense = Expense(vendor=template.vendor, category=template.category)
    if "date" in request.GET:
        expense.date = request.GET["date"]
    else:
        expense.date = today_date()

    if template.type == "count":
        if not request.GET.get("count"):
            count = decimal.Decimal(1)
        else:
            count = parse_amount_input(request.GET["count"])
            if count is None:
                return HttpResponseBadRequest()

        expense.amount = round_money(template.amount * count)
        desc_lines = template.description.strip().split("\n")
        desc_possibilities = len(desc_lines)
        desc = desc_lines[0]
        if count % 1 != 0:
            # Is decimal, use last possibility
            desc = desc_lines[desc_possibilities - 1]
        elif desc_possibilities == 2:
            # 0 → 1, 1 → anything else (English)
            desc = desc_lines[int(count != 1)]
        elif desc_possibilities in {3, 4}:
            # Polish scheme
            if count == 1:
                desc = desc_lines[0]
            else:
                # Expression from gettext, simplified
                desc = desc_lines[1 if (2 <= count % 10 <= 4 and (count % 100 < 10 or count % 100 >= 20)) else 2]

        expense.description = desc.replace("!count!", str(count))
    elif template.type == "description":
        expense.amount = template.amount
        expense.description = template.description.replace("!description!", request.GET["description"])
    elif template.type == "desc_select":
        main_desc, *desc_options = template.description.strip().split("\n")
        desc_id = int(request.GET["desc_id"])
        expense.amount = template.amount
        expense.description = main_desc.strip().replace("!description!", desc_options[desc_id].strip())
    elif template.type == "menu":
        desc_id = int(request.GET["desc_id"])
        desc_options = template.description.strip().split("\n")
        amount_str, desc = desc_options[desc_id].strip().split(" ", 1)
        expense.amount = parse_amount_input(amount_str.strip())
        if expense.amount is None:
            return HttpResponseBadRequest()
        expense.description = desc.strip()
    else:
        expense.amount = template.amount
        expense.description = template.description

    expense.user = request.user
    expense.save()
    return HttpResponseRedirect(expense.get_absolute_url())
예제 #11
0
def new(request):
    context = RequestContext(
        request, {
            'expense': Expense(),
            'F_checked': None,
            'C_checked': None,
            'H_checked': None
        })
    return render(request, 'expenses/new.html', context)
예제 #12
0
    def execute(self) -> Expense:
        expense = Expense(id=self.id,
                          title=self.title,
                          amount=self.amount,
                          created_at=self.created_at,
                          tags=self.tags,
                          email=self.email).EditExpense(id=self.id)

        return expense
예제 #13
0
 def create_expense(self):
     ex = Expense(
         description=self.fake.sentence(),
         date=self.fake.date(),
         category=random.choice(self.CATEGORY_OPTIONS),
         owner=User.objects.first(),
         amount=self.fake.latitude()
     )
     ex.save()
예제 #14
0
 def _create_dummy_expenses(self, user1, user2):
     now = datetime.datetime.now()
     few_days_ago = now - datetime.timedelta(days=5)
     last_month = now - datetime.timedelta(days=35)
     expense1 = Expense(user=user1, item='Urban Plates', cost=13.34, date_purchased=now,
                        category='Food', subcategory='Eat Out')
     expense1.save()
     expense2 = Expense(user=user1, item='Gas', cost=45, date_purchased=few_days_ago,
                        category='Car', subcategory='Gas')
     expense2.save()
     expense3 = Expense(user=user1, item='Amazon', cost=64.4, date_purchased=last_month,
                        category='Entertainment', subcategory='One-Timer')
     expense3.save()
     expense4 = Expense(user=user2, item='Vons', cost=25, date_purchased=now,
                        category='Food', subcategory='Grocery')
     expense4.save()
     expense5 = Expense(user=user2, item='Beer', cost=14, date_purchased=few_days_ago,
                        category='Entertainment', subcategory='Party')
     expense5.save()
예제 #15
0
 def test_expense(self):
     o = Expense(
         title="Mercedes",
         amount=212_000,
         date=f"2020-02-23",
         description="",
     )
     o.full_clean()
     o.save()
     self.assertTrue(o.is_expensive())
예제 #16
0
def bill_quickadd(request):
    if request.method != "POST":
        return HttpResponseNotAllowed(["POST"])
    quickadd_str = request.POST.get("quickadd")
    if quickadd_str is None:
        return HttpResponseBadRequest()
    category_id, vendor = quickadd_str.split(";", 1)
    inst = Expense(user=request.user, category_id=category_id, vendor=vendor, is_bill=True)
    inst.save()
    return HttpResponseRedirect(reverse("expenses:bill_show", args=[inst.pk]))
예제 #17
0
 def handle(self, n, **options):
     for i in range(n):
         o = Expense()
         o.title = silly.a_thing()
         o.amount = "{}.{}".format(random.randint(1, 100),
                                   random.randint(0, 99))
         o.date = silly.datetime().date()
         o.description = "\n".join(
             [silly.paragraph(),
              silly.paragraph(),
              silly.paragraph()])
         o.save()
예제 #18
0
def test_list_expenses():
    """
    GIVEN 2 expenses stored in the database
    WHEN the execute method is called
    THEN it should return 2 expenses
    """
    Expense(title='New Egdsahggewxpense',
            amount=12.0,
            created_at='12/08/1994',
            tags='daifdsafdsary',
            email='*****@*****.**').AddExpense()

    expense = Expense(title='New Expense',
                      amount=12.0,
                      created_at='12/08/1994',
                      tags='dairy',
                      email='*****@*****.**').AddExpense()

    query = ListExpensesQuery(email=expense.email)

    assert len(query.execute()) == 2
예제 #19
0
    def test_can_not_spend_without_spent_by(self):
        """
        A spent_by is always required
        """
        spent_for = Person(username="******")
        budget_group = BudgetGroup()

        expense = Expense(spent_for=spent_for,
                          budget_group=budget_group,
                          amount_spent=100)

        self.assertRaises(Person.DoesNotExist, expense.clean)
예제 #20
0
    def test_can_add_expense_from_person_to_group(self):
        """
        A person can spent money for a budget group
        """
        spent_by = Person(username="******")
        budget_group = BudgetGroup()

        expense = Expense(spent_by=spent_by,
                          budget_group=budget_group,
                          amount_spent=100)

        self.assertIsNotNone(expense)
예제 #21
0
 def _populate_expense_table(self, df, users):
     """DF --> DB"""
     for i, row in df.iterrows():
         for user in users:
             expense = Expense(
                 user=user,
                 item=row['item'],
                 cost=row['cost'],
                 date_purchased=row['date_purchased'],
                 category=row['category'],
                 subcategory=row['subcategory']
             )
             expense.save()
예제 #22
0
    def test_can_not_spend_zero_amount(self):
        """
        Creating a negative expense is not possible
        """
        spent_by = Person(username="******")
        spent_for = Person(username="******")
        budget_group = BudgetGroup()

        expense = Expense(spent_by=spent_by,
                          spent_for=spent_for,
                          budget_group=budget_group,
                          amount_spent=0)

        self.assertRaises(ValidationError, expense.clean)
예제 #23
0
    def test_can_add_expense_from_person_for_person(self):
        """
        A person can spent money for another person
        """
        spent_by = Person(username="******")
        spent_for = Person(username="******")
        budget_group = BudgetGroup()

        expense = Expense(spent_by=spent_by,
                          spent_for=spent_for,
                          budget_group=budget_group,
                          amount_spent=100)

        self.assertIsNotNone(expense)
def expense_repeat(request, pk):
    old_expense = get_object_or_404(Expense, pk=pk, user=request.user, is_bill=False)
    new_expense = Expense(
        date=today_date(),
        vendor=old_expense.vendor,
        category=old_expense.category,
        amount=old_expense.amount,
        description=old_expense.description,
        user=request.user,
        is_bill=False,
    )
    new_expense.save()
    messages.add_message(request, messages.SUCCESS, _("Expense has been repeated successfully."))
    return HttpResponseRedirect(reverse("expenses:expense_show", args=[new_expense.pk]))
예제 #25
0
    def handle(self, n, *args, **kwargs):
        Expense.objects.all().delete()
        for i in range(n):
            with transaction.atomic():
                o = Expense()
                o.title = silly.a_thing()
                o.date = silly.datetime().date()
                o.amount = f"{random.uniform(1, 1000):.2f}"
                o.comment = "\n".join(silly.paragraph()
                                      for i in range(random.randrange(0, 3)))
                o.save()

                for j in range(random.randrange(5)):
                    o.notes.create(content="\n".join(
                        silly.paragraph()
                        for i in range(random.randrange(1, 4))))
예제 #26
0
def test_get_expense_by_id():
    """
    GIVEN ID of article stored in the database
    WHEN the execute method is called GetExpenseByIDQuery with id set
    THEN it should return the expense with the same id
    """
    expense = Expense(title='New Expense',
                      amount=12.0,
                      created_at='12/08/1994',
                      tags='dairy',
                      email='*****@*****.**').AddExpense()

    query = GetExpenseByIDQuery(id=expense.id, email=expense.email).execute()

    assert query.title == expense.title
    assert query.amount == expense.amount
    assert query.created_at == expense.created_at
    assert query.tags == expense.tags
예제 #27
0
def index():
    template_names = []
    default_fields = None
    form = ExpensesForm(request.form)
    default_template_name = ''
    today = datetime.datetime.now()

    if request.method == 'POST':
        if form.validate_on_submit():
            for item in form.items.data:
                entry = Expense(item['expense'],
                                item['cost'],
                                item['due_date'],
                                item['desc'],
                                get_user_id()
                                )
                db.session.add(entry)
                db.session.commit()
        return redirect(url_for('main.index'))
    
    six_months = get_data(db, Expense, session, today)

    yty_data = dict([(i.due_date.strftime('%Y%m%d'), []) for i in six_months])
    for i in six_months:
        yty_data[i.due_date.strftime('%Y%m%d')].append({'expense': i.expense,
                                                        'cost': i.cost,
                                                        'expense_type': i.expense_type
                                                        })
    try:
        default_fields = Template.query.filter_by(user_id=session['user_id'],
                                                  default=True
                                                  ).one()
        default_template_name = default_fields.name
        default_fields = default_fields.template

    except Exception as e:
        print(e)

    for i in Template.query.filter_by(user_id=session['user_id']).all():
        template_names.append(i.name)
    return render_template('index.html', form=form,
                           expenses=yty_data, name=current_user.username,
                           default_fields=default_fields, template_names=template_names,
                           default_template_name=default_template_name)
예제 #28
0
    def create_random_expense(self):
        with transaction.atomic():
            o = Expense()
            o.title = silly.a_thing()
            o.amount = "{}.{}".format(random.randint(1, 100),
                                      random.randint(0, 99))
            o.date = silly.datetime().date()
            o.description = "\n".join(
                [silly.paragraph(),
                 silly.paragraph(),
                 silly.paragraph()])
            o.save()

            for j in range(random.randint(0, 5)):
                text = "\n".join(
                    [silly.paragraph(),
                     silly.paragraph(),
                     silly.paragraph()])
                o.comments.create(content=text)
예제 #29
0
def test_list_expenses(client):
    """
    GIVEN expenses stored in the database
    WHEN endpoint /expense-list/ is called
    THEN it should return list of Expense in json format matching schema
    """
    Expense(title='New Expense',
            amount=12.0,
            created_at='12/08/1994',
            tags='dairy',
            email='*****@*****.**').AddExpense()

    data = {'email': '*****@*****.**'}

    response = client.get(
        '/expense-list/',
        data=json.dumps(data),
        content_type='application/json',
    )
    validate_payload(response.json, 'ExpenseList.json')
예제 #30
0
def new_expense(params, user, group):
    """
    Check if params are valid and create a new expense.

    Returns a text to send to the user.
    """
    try:
        data = decode_expense_params(params, group)
    except ParameterError as e:
        return str(e)

    amount = data['amount']
    description = data['description']
    date = data['dd']
    tags = data['tt']
    expense = Expense(user=user, group=group, description=description, amount=amount, date=date)
    expense.save()
    if tags:
        for tag in tags:
            expense.tags.add(tag)

    return 'Se guardó tu gasto {}'.format(expense)