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)
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)
def expense(request, id=0): """ Get or Create/Update an expense """ status = 400 expense = None errors = {} if request.method == 'PUT': data = json.loads(request.body) expense = get_object_or_404(Expense, pk=id, user=request.user) form = ExpenseForm(data, instance=expense) if form.is_valid(): balance = Balance.objects.get(user=request.user) # Reset balance because we're updating an expense. balance.amount -= expense.amount form.save() balance.amount += expense.amount balance.save() status = 200 else: errors = {'errors':form.errors} elif request.method == 'POST': data = json.loads(request.body) form = ExpenseForm(data) if form.is_valid(): form.cleaned_data['user'] = request.user expense = Expense(**form.cleaned_data) expense.save() try: balance = Balance.objects.get(user=request.user) except Balance.DoesNotExist: # This user doesn't have a balance yet, create one. balance = Balance.objects.create(amount=0.0, user=request.user) balance.amount += expense.amount balance.save() status = 201 else: errors = {'errors':form.errors} elif request.method == 'DELETE': expense = get_object_or_404(Expense, pk=id, user=request.user) try: # Make sure to update balance depending on the transaction type. balance = Balance.objects.get(user=request.user) balance.amount -= expense.amount balance.save() expense.delete() except: status = 500 return HttpResponse(status=status) else: try: expense = get_object_or_404(Expense, pk=id) except Expense.DoesNotExist: logging.error('expense does not exist: ' + id) status = 404 response = errors if errors else serialize(expense, ignore=[auth.models.User]) return HttpResponse(content=json.dumps(response), status=status, content_type='application/json')
def handle_uploaded_file(self, f): csv_buffer = StringIO(f.read().decode()) reader = csv.reader(csv_buffer) header = next(reader, None) parser = ParserFactory(header).create() """ for row in reader: count += 1 if count == 1: # TODO: Use CSV Sniffer header = row for header_type, headers in ParserFactory.HEADERS.items(): first_header = header[0].replace('\ufeff', '').replace('"', '') if first_header == headers[0]: parsing = header_type break if parsing: continue parser = ParserFactory().get(parsing) if not parsing or not parser: break parser.parse(row) """ for row in reader: parser.parse(row) Expense.assign_categories()
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)
def index(request): year = int(request.GET.get('year', datetime.now().year)) monthly = Expense.monthly(year) context = { 'year_range': Expense.year_range(), 'monthly_expenses': monthly, } return render(request, 'expenses/index.html', context)
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()
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]))
def index(request): year = int(request.GET.get('year', datetime.now().year)) monthly = Expense.monthly(year) context = { 'year_range': Expense.year_range(), 'monthly_expenses': monthly, } return render( request, 'expenses/index.html', context)
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()
def test_assign_categories(self): for i in range(0, 3): expense = Expense.objects.create( description='Spotify {}'.format(i + 1), payment=constants.CREDIT_CARD, amount=Decimal('9.99'), date=datetime.now(), ) self.assertIsNone(expense.category) Expense.assign_categories() for expense in Expense.objects.all(): self.assertEqual(expense.category.name, 'Entertainment')
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]))
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)
def test_expense_too_small(self): o = Expense( title="Bazooka", amount=0.02, date=f"2020-02-23", ) self.assertRaises(ValidationError, o.full_clean)
def details_json(request, slug): df = Expense.data_frame() categories = df.groupby('Category') category = Category.objects.get(slug=slug).name yearly = categories.get_group(category).resample('A').sum() yearly = yearly.fillna(0) monthly = categories.get_group(category).resample('M').sum() monthly = monthly.fillna(0) monthly_mean = monthly.mean()['Amount'] yearly_mean = yearly.mean()['Amount'] yearly.index = yearly.index.map(lambda t: t.strftime('%Y')) monthly.index = monthly.index.map(lambda t: t.strftime('%b %Y')) category_data = { 'name': category, 'monthly_mean': '{0:.2f}'.format(monthly_mean), 'sum': '{0:.2f}'.format(categories.get_group(category).sum()['Amount']), 'yearly_mean': '{0:.2f}'.format(yearly_mean), } json_data = { 'category': category_data, 'monthly': monthly.to_dict(into=collections.OrderedDict), 'yearly': yearly.to_dict() } return JsonResponse(json_data)
def cuisines(request): days = 30 past = datetime.now() - timedelta(days=days) expenses = Expense.objects.filter(category__name='Restaurant', date__gte=past).order_by('-date') last_year = Expense.objects.filter(category__name='Restaurant', date__gte=past - timedelta(days=365), date__lte=datetime.now() - timedelta(days=365)).order_by('-date') df = Expense.data_frame() top_cuisines = df.groupby(['Cuisine' ]).count().sort_values('Category', ascending=False).head(40) context = { 'days': days, 'expenses': expenses, 'last_year': last_year, 'top_cuisines': top_cuisines['Category'].to_dict(into=collections.OrderedDict) } return render(request, 'expenses/cuisines.html', context)
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)
def test_edit_expense(): """ GIVEN EditExpenseCommand with a valid properties title, amount, created_at and content WHEN the execute method is called THEN a new Expense must exist in the database with the same attributes """ expense = CreateExpenseCommand(title='New Expense', amount=12.0, created_at='12/08/1994', tags='dairy', email='*****@*****.**').execute() edit_expense = EditExpenseCommand(id=expense.id, title='New Expense edit', amount=24.0, created_at='12/08/1995', tags='dairyyyy', email=expense.email).execute() edit_expense_check = Expense.GetExpenseByID(id=expense.id, email=expense.email) assert edit_expense_check.id == edit_expense.id assert edit_expense_check.title == edit_expense.title assert edit_expense_check.amount == edit_expense.amount assert edit_expense_check.created_at == edit_expense.created_at assert edit_expense_check.tags == edit_expense.tags assert edit_expense_check.email == edit_expense.email
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
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
def new(request): context = RequestContext( request, { 'expense': Expense(), 'F_checked': None, 'C_checked': None, 'H_checked': None }) return render(request, 'expenses/new.html', context)
def category_stats_json(request): df = Expense.data_frame() categories = df.groupby('Category') data = {} for category in Category.objects.all(): if category.name in categories.groups: data[category.name] = '{:.2f}'.format(categories.get_group(category.name).sum()['Amount']) return JsonResponse(data)
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
def descriptions(request): keyword = request.GET.get('term', '') if keyword: expenses = Expense.cached().filter(description__icontains=keyword).order_by('description').distinct('description') data = [expense.description for expense in expenses] else: data = [] return HttpResponse(json.dumps(data), content_type='application/javascript')
def account_overview(request): return render(request, 'admin/account/overview.html', { 'expenses': json.dumps( [expense.to_dict() for expense in Expense.accountable(request.user.profile.may_account())], default=json_serial), 'invoices': json.dumps( [invoice.to_dict() for invoice in Invoice.accountable(request.user.profile.may_account())], default=json_serial) })
def pay_overview(request): """ Shows a list of all payable expenses and lets user pay them. """ return render(request, 'admin/pay/overview.html', { 'expenses': json.dumps([expense.to_dict() for expense in Expense.payable()], default=json_serial), 'invoices': json.dumps([invoice.to_dict() for invoice in Invoice.payable()], default=json_serial), 'accounts': json.dumps([s.name for s in BankAccount.objects.all().order_by('name')]) })
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()
def category_stats_json(request): df = Expense.data_frame() categories = df.groupby('Category') data = {} for category in Category.objects.all(): if category.name in categories.groups: data[category.name] = '{:.2f}'.format( categories.get_group(category.name).sum()['Amount']) return JsonResponse(data)
def category(request): description = request.GET.get('description', '') data = { 'category_id': 0, } if description: expenses = Expense.cached().filter(description=description) if expenses: data['category_id'] = expenses[0].category.id return HttpResponse(json.dumps(data), content_type='application/javascript')
def attest_overview(request): """ Displays the attest overview list. """ return render(request, 'admin/attest/overview.html', { 'expenses': json.dumps( [expense.to_dict() for expense in Expense.attestable(request.user.profile.may_attest(), request.user)], default=json_serial), 'invoices': json.dumps( [invoice.to_dict() for invoice in Invoice.attestable(request.user.profile.may_attest(), request.user)], default=json_serial) })
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
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)
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)
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())
def descriptions(request): keyword = request.GET.get('term', '') if keyword: expenses = Expense.cached().filter( description__icontains=keyword).order_by('description').distinct( 'description') data = [expense.description for expense in expenses] else: data = [] return HttpResponse(json.dumps(data), content_type='application/javascript')
def load_expenses(self): self.download_file() with open(self.output_path, 'r') as csvfile: reader = csv.reader(csvfile) for row in reader: if len(row) < 7: continue try: transaction_date = datetime.strptime(row[0], '%Y-%m-%d') except ValueError: continue amount = Decimal(row[1]) * Decimal(-1) description = row[7].title() if Expense.objects.filter(description=description, date=transaction_date, amount=amount).count(): continue Expense.objects.create( description=description, payment=constants.CASH, amount=amount, date=transaction_date, ) Expense.assign_categories()
def handle(self, *args, **options): while User.objects.count() < 10: User.objects.create_user( username=silly.firstname().lower(), password="******", ) users = list(User.objects.all()) n = 10 for i in range(n): o = Expense( user=random.choice(users), created_at=silly.datetime(), title="{} {}".format(silly.adjective(), silly.noun()), amount=str(silly.number() + silly.number() / 10)[:5], description=silly.sentence() + "\n" + silly.sentence() ) o.full_clean() o.save() o.created_at = silly.datetime() o.save()
def trends(request): context = {'expenses': []} if request.method == 'POST': form = TrendsForm(request.POST) if form.is_valid(): description = form.cleaned_data['description'] expenses = Expense.cached().filter(description__icontains=description).order_by('-date') context['sum'] = expenses.aggregate(expenses_sum=Sum('amount')).get('expenses_sum', Decimal(0)) context['expenses'] = expenses else: form = TrendsForm() context['form'] = form return render( request, 'expenses/trends.html', context, )
def index(request): month = int( request.GET.get("month") or date.today().month ) year = int( request.GET.get("year") or date.today().year ) user = UserUtil.get_current_user; resume = { "expenses": Expense.total_for(user,month,year)['value__sum'] or 0.0, "profits" : Profit.total_for(user,month,year)["value__sum"] or 0.0 } month_expenses = ExpenseType.month_resume( user=user, month=month, year=year ); return render( request , "main/index.html" , { "resume" : resume, "month_expenses": month_expenses, "years" : DateUtil.YEARS, "months" : DateUtil.MONTHS, "selected_month" : month, "selected_year" : year, })
def cuisines(request): days = 30 past = datetime.now() - timedelta(days=days) expenses = Expense.objects.filter( category__name='Restaurant', date__gte=past).order_by('-date') last_year = Expense.objects.filter( category__name='Restaurant', date__gte=past - timedelta(days=365), date__lte=datetime.now() - timedelta(days=365)).order_by('-date') df = Expense.data_frame() top_cuisines = df.groupby(['Cuisine']).count().sort_values('Category', ascending=False).head(40) context = { 'days': days, 'expenses': expenses, 'last_year': last_year, 'top_cuisines': top_cuisines['Category'].to_dict(into=collections.OrderedDict) } return render( request, 'expenses/cuisines.html', context)
def test_cached(self): self.assertEqual(Expense.cached().count(), 1)
def test_data_frame(self): df = Expense.data_frame() self.assertEqual(df.Description[0], 'Spotify')
def test_year_range(self): self.assertEqual(Expense.year_range(), [2016])
def test_monthly(self): output = Expense.monthly() self.assertEqual(output, [(datetime(2016, 2, 1, 0, 0), [Expense.objects.get(pk=1)])])
def test_write_csv(self): Expense.write_csv() fp = open(DATA_PATH, 'r') self.assertTrue('Spotify,Entertainment' in fp.read()) fp.close()
def post(self, request, *args, **kwargs): purchase_dict = ast.literal_eval(request.POST['purchase']) purchase, purchase_created = Purchase.objects.get_or_create(purchase_invoice_number=purchase_dict['purchase_invoice_number']) purchase.purchase_invoice_number = purchase_dict['purchase_invoice_number'] purchase.vendor_invoice_number = purchase_dict['vendor_invoice_number'] purchase.vendor_do_number = purchase_dict['vendor_do_number'] purchase.vendor_invoice_date = datetime.strptime(purchase_dict['vendor_invoice_date'], '%d/%m/%Y') purchase.purchase_invoice_date = datetime.strptime(purchase_dict['purchase_invoice_date'], '%d/%m/%Y') brand = Brand.objects.get(brand=purchase_dict['brand']) purchase.brand = brand vendor = Vendor.objects.get(user__first_name=purchase_dict['vendor_name']) transport = TransportationCompany.objects.get(company_name=purchase_dict['transport']) purchase.vendor = vendor purchase.transportation_company = transport if purchase_dict['discount']: purchase.discount = purchase_dict['discount'] else: purchase.discount = 0 purchase.net_total = purchase_dict['net_total'] purchase.purchase_expense = purchase_dict['purchase_expense'] purchase.grant_total = purchase_dict['grant_total'] vendor_account, vendor_account_created = VendorAccount.objects.get_or_create(vendor=vendor) if vendor_account_created: vendor_account.total_amount = purchase_dict['vendor_amount'] vendor_account.balance = purchase_dict['vendor_amount'] else: if purchase_created: vendor_account.total_amount = vendor_account.total_amount + purchase_dict['vendor_amount'] vendor_account.balance = vendor_account.balance + purchase_dict['vendor_amount'] else: vendor_account.total_amount = vendor_account.total_amount - purchase.vendor_amount + purchase_dict['vendor_amount'] vendor_account.balance = vendor_account.balance - purchase.vendor_amount + purchase_dict['vendor_amount'] vendor_account.save() purchase.vendor_amount = purchase_dict['vendor_amount'] purchase.save() # Save purchase_expense in Expense if Expense.objects.exists(): voucher_no = int(Expense.objects.aggregate(Max('voucher_no'))['voucher_no__max']) + 1 else: voucher_no = 1 if not voucher_no: voucher_no = 1 expense = Expense() expense.created_by = request.user expense.expense_head, created = ExpenseHead.objects.get_or_create(expense_head = 'purchase') expense.date = dt.datetime.now().date().strftime('%Y-%m-%d') expense.voucher_no = voucher_no expense.amount = purchase_dict['purchase_expense'] expense.payment_mode = 'cash' expense.narration = 'By purchase' expense.save() purchase_items = purchase_dict['purchase_items'] deleted_items = purchase_dict['deleted_items'] for p_item in deleted_items: item = Item.objects.get(code = p_item['item_code']) ps_item = PurchaseItem.objects.get(item=item) inventory = Inventory.objects.get(item=item) inventory.quantity = inventory.quantity + ps_item.quantity_purchased inventory.save() ps_item.delete() for purchase_item in purchase_items: item = Item.objects.get(code=purchase_item['item_code']) p_item, item_created = PurchaseItem.objects.get_or_create(item=item, purchase=purchase) inventory, created = Inventory.objects.get_or_create(item=item) if created: inventory.quantity = int(purchase_item['qty_purchased']) else: if purchase_created: inventory.quantity = inventory.quantity + int(purchase_item['qty_purchased']) else: inventory.quantity = inventory.quantity - p_item.quantity_purchased + int(purchase_item['qty_purchased']) inventory.selling_price = purchase_item['selling_price'] inventory.unit_price = purchase_item['unit_price'] inventory.discount_permit_percentage = purchase_item['permit_disc_percent'] inventory.discount_permit_amount = purchase_item['permit_disc_amt'] inventory.vendor = vendor inventory.save() p_item, item_created = PurchaseItem.objects.get_or_create(item=item, purchase=purchase) p_item.purchase = purchase p_item.item = item p_item.quantity_purchased = purchase_item['qty_purchased'] p_item.item_frieght = purchase_item['frieght'] p_item.frieght_per_unit = purchase_item['frieght_unit'] p_item.item_handling = purchase_item['handling'] p_item.handling_per_unit = purchase_item['handling_unit'] p_item.expense = purchase_item['expense'] p_item.expense_per_unit = purchase_item['expense_unit'] p_item.cost_price = purchase_item['cost_price'] p_item.net_amount = purchase_item['net_amount'] p_item.save() res = { 'result': 'Ok', } response = simplejson.dumps(res) status_code = 200 return HttpResponse(response, status = status_code, mimetype="application/json")