Exemple #1
0
    def test_get_mint_updates_no_itemize_arg_three_items(self):
        i1 = item(
            title='Really cool watch',
            quantity=1,
            item_subtotal='$10.00',
            item_subtotal_tax='$1.00',
            item_total='$11.00')
        i2 = item(
            title='Organic water',
            quantity=1,
            item_subtotal='$6.00',
            item_subtotal_tax='$0.00',
            item_total='$6.00')
        o1 = order(
            subtotal='$16.00',
            tax_charged='$1.00',
            total_charged='$17.00')
        t1 = transaction(amount='$17.00')

        stats = Counter()
        updates, _ = tagger.get_mint_updates(
            [o1], [i1, i2], [],
            [t1],
            get_args(no_itemize=True), stats)

        self.assertEqual(len(updates), 1)
        orig_t, new_trans = updates[0]
        self.assertTrue(orig_t is t1)
        self.assertEqual(len(new_trans), 1)
        self.assertEqual(new_trans[0].merchant,
                         'Amazon.com: Really cool watch, Organic water')
        self.assertEqual(new_trans[0].category, 'Shopping')
        self.assertEqual(new_trans[0].amount, 17000000)
Exemple #2
0
    def test_get_mint_updates_verbose_itemize_arg(self):
        i1 = item()
        o1 = order(shipping_charge='$3.99', total_promotions='$3.99')
        t1 = transaction()

        stats = Counter()
        updates, _ = tagger.get_mint_updates(
            [o1], [i1], [],
            [t1],
            get_args(verbose_itemize=True), stats)

        self.assertEqual(len(updates), 1)
        orig_t, new_trans = updates[0]
        self.assertTrue(orig_t is t1)
        self.assertEqual(len(new_trans), 3)
        self.assertEqual(new_trans[0].merchant, 'Amazon.com: Promotion(s)')
        self.assertEqual(new_trans[0].category, 'Shipping')
        self.assertEqual(new_trans[0].amount, -3990000)
        self.assertFalse(new_trans[0].is_debit)
        self.assertEqual(new_trans[1].merchant, 'Amazon.com: Shipping')
        self.assertEqual(new_trans[1].category, 'Shipping')
        self.assertEqual(new_trans[1].amount, 3990000)
        self.assertEqual(new_trans[2].merchant, 'Amazon.com: 2x Duracell AAs')
        self.assertEqual(new_trans[2].category, 'Shopping')
        self.assertEqual(new_trans[2].amount, 11950000)

        self.assertEqual(stats['new_tag'], 1)
Exemple #3
0
    def test_get_mint_updates_multi_orders_trans_same_date_and_amount(self):
        i1 = item(order_id='A')
        o1 = order(order_id='A')
        i2 = item(order_id='B')
        o2 = order(order_id='B')
        t1 = transaction()
        t2 = transaction()

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1, o2], [i1, i2], [], [t1, t2],
                                             get_args(), stats)

        self.assertEqual(len(updates), 2)

        updates2, _ = tagger.get_mint_updates([o1, o2], [i1, i2], [], [t1, t2],
                                              get_args(num_updates=1), stats)

        self.assertEqual(len(updates2), 1)
Exemple #4
0
    def test_get_mint_updates_multi_domains_no_retag(self):
        i1 = item()
        o1 = order()
        t1 = transaction(merchant='Amazon.co.uk: already tagged')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1], [i1], [], [t1], get_args(),
                                             stats)

        self.assertEqual(len(updates), 0)
Exemple #5
0
    def test_get_mint_updates_retag_arg(self):
        i1 = item()
        o1 = order()
        t1 = transaction(merchant='Amazon.com: already tagged')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1], [i1], [], [t1],
                                             get_args(retag_changed=True),
                                             stats)

        self.assertEqual(len(updates), 1)
        self.assertEqual(stats['retag'], 1)
Exemple #6
0
    def test_get_mint_updates_refund_no_date(self):
        r1 = refund(title='Cool item2',
                    refund_amount='$10.95',
                    refund_tax_amount='$1.00',
                    refund_date=None)
        t1 = transaction(amount='$11.95', is_debit=False, date='3/12/14')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([], [], [r1], [t1], get_args(),
                                             stats)

        self.assertEqual(len(updates), 0)
        self.assertEqual(stats['new_tag'], 0)
Exemple #7
0
    def test_get_mint_updates_no_tag_categories_arg(self):
        i1 = item()
        o1 = order()
        t1 = transaction(merchant='Amazon.com: 2x Duracell AAs',
                         note=o1.get_note() + '\nItem(s):\n - 2x Duracell AAs')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1], [i1], [], [t1],
                                             get_args(no_tag_categories=True),
                                             stats)

        self.assertEqual(len(updates), 0)
        self.assertEqual(stats['already_up_to_date'], 1)
Exemple #8
0
    def test_get_mint_updates_skip_already_tagged(self):
        i1 = item()
        o1 = order()
        t1 = transaction(merchant='SomeRandoCustomPrefix: already tagged')

        stats = Counter()
        updates, _ = tagger.get_mint_updates(
            [o1], [i1], [], [t1],
            get_args(description_prefix_override='SomeRandoCustomPrefix: '),
            stats)

        self.assertEqual(len(updates), 0)
        self.assertEqual(stats['no_retag'], 1)
Exemple #9
0
    def test_get_mint_updates_no_update_for_identical(self):
        i1 = item()
        o1 = order()
        t1 = transaction(merchant='Amazon.com: 2x Duracell AAs',
                         category='Electronics & Software',
                         note=o1.get_note() + '\nItem(s):\n - 2x Duracell AAs')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1], [i1], [], [t1],
                                             get_args(retag_changed=True),
                                             stats)

        self.assertEqual(len(updates), 0)
        self.assertEqual(stats['already_up_to_date'], 1)
Exemple #10
0
    def test_get_mint_updates_no_itemize_arg_single_item(self):
        i1 = item()
        o1 = order(total_charged='$15.94', shipping_charge='$3.99')
        t1 = transaction(amount='$15.94')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1], [i1], [], [t1],
                                             get_args(no_itemize=True), stats)

        self.assertEqual(len(updates), 1)
        orig_t, new_trans = updates[0]
        self.assertTrue(orig_t is t1)
        self.assertEqual(len(new_trans), 1)
        self.assertEqual(new_trans[0].merchant, 'Amazon.com: 2x Duracell AAs')
        self.assertEqual(new_trans[0].category, 'Electronics & Software')
        self.assertEqual(new_trans[0].amount, 15940000)
Exemple #11
0
    def test_get_mint_updates_simple_match(self):
        i1 = item()
        o1 = order()
        t1 = transaction()

        stats = Counter()
        updates, _ = tagger.get_mint_updates([o1], [i1], [], [t1], get_args(),
                                             stats)

        self.assertEqual(len(updates), 1)
        orig_t, new_trans = updates[0]
        self.assertTrue(orig_t is t1)
        self.assertEqual(len(new_trans), 1)
        self.assertEqual(new_trans[0].merchant, 'Amazon.com: 2x Duracell AAs')
        self.assertEqual(new_trans[0].category, 'Electronics & Software')
        self.assertEqual(new_trans[0].amount, 11950000)
        self.assertTrue(new_trans[0].is_debit)
        self.assertFalse(new_trans[0].is_child)

        self.assertEqual(stats['new_tag'], 1)
Exemple #12
0
    def test_get_mint_updates_simple_match_refund(self):
        r1 = refund(title='Cool item',
                    refund_amount='$10.95',
                    refund_tax_amount='$1.00',
                    refund_date='3/12/14')
        t1 = transaction(amount='$11.95', is_debit=False, date='3/12/14')

        stats = Counter()
        updates, _ = tagger.get_mint_updates([], [], [r1], [t1], get_args(),
                                             stats)

        self.assertEqual(len(updates), 1)
        orig_t, new_trans = updates[0]
        self.assertTrue(orig_t is t1)
        self.assertEqual(len(new_trans), 1)
        self.assertEqual(new_trans[0].merchant, 'Amazon.com: 2x Cool item')
        self.assertEqual(new_trans[0].category, 'Returned Purchase')
        self.assertEqual(new_trans[0].amount, -11950000)
        self.assertFalse(new_trans[0].is_debit)
        self.assertFalse(new_trans[0].is_child)

        self.assertEqual(stats['new_tag'], 1)
Exemple #13
0
def main():
    warn_if_outdated('mint-amazon-tagger', VERSION)
    is_outdated, latest_version = check_outdated('mint-amazon-tagger', VERSION)
    if is_outdated:
        print('Please update your version by running:\n'
              'pip3 install mint-amazon-tagger --upgrade')

    parser = argparse.ArgumentParser(
        description='Tag Mint transactions based on itemized Amazon history.')
    define_args(parser)
    args = parser.parse_args()

    if args.version:
        print('mint-amazon-tagger {}\nBy: Jeff Prouty'.format(VERSION))
        exit(0)

    session_path = args.session_path
    if session_path.lower() == 'none':
        session_path = None

    items_csv = args.items_csv
    orders_csv = args.orders_csv
    refunds_csv = args.refunds_csv

    start_date = None
    if not items_csv or not orders_csv:
        logger.info('Missing Items/Orders History csv. Attempting to fetch '
                    'from Amazon.com.')
        start_date = args.order_history_start_date
        duration = datetime.timedelta(days=args.order_history_num_days)
        end_date = datetime.date.today()
        # If a start date is given, adjust the end date based on num_days,
        # ensuring not to go beyond today.
        if start_date:
            start_date = start_date.date()
            if start_date + duration < end_date:
                end_date = start_date + duration
        else:
            start_date = end_date - duration
        items_csv, orders_csv, refunds_csv = fetch_order_history(
            args.report_download_location, start_date, end_date,
            args.amazon_email, args.amazon_password, session_path,
            args.headless)

    if not items_csv or not orders_csv:  # Refunds are optional
        logger.critical('Order history either not provided at command line or '
                        'unable to fetch. Exiting.')
        exit(1)

    orders = amazon.Order.parse_from_csv(orders_csv,
                                         ProgressCounter('Parsing Orders - '))
    items = amazon.Item.parse_from_csv(items_csv,
                                       ProgressCounter('Parsing Items - '))
    refunds = ([] if not refunds_csv else amazon.Refund.parse_from_csv(
        refunds_csv, ProgressCounter('Parsing Refunds - ')))

    if args.dry_run:
        logger.info('\nDry Run; no modifications being sent to Mint.\n')

    # Initialize the stats. Explicitly initialize stats that might not be
    # accumulated (conditionals).
    stats = Counter(
        adjust_itemized_tax=0,
        already_up_to_date=0,
        misc_charge=0,
        new_tag=0,
        no_retag=0,
        retag=0,
        user_skipped_retag=0,
        personal_cat=0,
    )

    mint_client = MintClient(args.mint_email, args.mint_password, session_path,
                             args.headless, args.mint_mfa_method,
                             args.wait_for_sync)

    if args.pickled_epoch:
        mint_trans, mint_category_name_to_id = (
            get_trans_and_categories_from_pickle(args.pickled_epoch,
                                                 args.mint_pickle_location))
    else:
        # Get the date of the oldest Amazon order.
        if not start_date:
            start_date = min([o.order_date for o in orders])
            if refunds:
                start_date = min(start_date,
                                 min([o.order_date for o in refunds]))

        # Double the length of transaction history to help aid in
        # personalized category tagging overrides.
        today = datetime.date.today()
        start_date = today - (today - start_date) * 2
        mint_category_name_to_id = mint_client.get_categories()
        mint_transactions_json = mint_client.get_transactions(start_date)

        epoch = int(time.time())
        mint_trans = mint.Transaction.parse_from_json(mint_transactions_json)
        dump_trans_and_categories(mint_trans, mint_category_name_to_id, epoch,
                                  args.mint_pickle_location)

    updates, unmatched_orders = tagger.get_mint_updates(
        orders, items, refunds, mint_trans, args, stats,
        mint_category_name_to_id)

    log_amazon_stats(items, orders, refunds)
    log_processing_stats(stats)

    if args.print_unmatched and unmatched_orders:
        logger.warning(
            'The following were not matched to Mint transactions:\n')
        by_oid = defaultdict(list)
        for uo in unmatched_orders:
            by_oid[uo.order_id].append(uo)
        for unmatched_by_oid in by_oid.values():
            orders = [o for o in unmatched_by_oid if o.is_debit]
            refunds = [o for o in unmatched_by_oid if not o.is_debit]
            if orders:
                print_unmatched(amazon.Order.merge(orders))
            for r in amazon.Refund.merge(refunds):
                print_unmatched(r)

    if not updates:
        logger.info(
            'All done; no new tags to be updated at this point in time!')
        exit(0)

    if args.dry_run:
        logger.info('Dry run. Following are proposed changes:')
        if args.skip_dry_print:
            logger.info('Dry run print results skipped!')
        else:
            tagger.print_dry_run(updates,
                                 ignore_category=args.no_tag_categories)

    else:
        mint_client.send_updates(updates,
                                 ignore_category=args.no_tag_categories)
Exemple #14
0
 def test_get_mint_updates_empty_input(self):
     updates, _ = tagger.get_mint_updates(
         [], [], [],
         [],
         get_args(), Counter())
     self.assertEqual(len(updates), 0)