def testInitView(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global dataset set populated
        self.assertEqual(gbl.dataset.get_emp_data(), test_data.employees)
        self.assertEqual(gbl.dataset.get_dept_data(), test_data.dept_objs)
        self.assertEqual(gbl.dataset.get_grant_admin_data(), test_data.grant_admin_objs)
        self.assertEqual(gbl.dataset.get_asn_data(), test_data.assignments)

        # verify dropdowns loaded
        self.assertEqual(self.view.dept_ctrl.GetItems(), test_data.department_items)
        self.assertEqual(self.view.grant_admin_ctrl.GetItems(), test_data.grant_admin_items)

        # verify the sent invoices are loaded
        self.assertEqual(gbl.dataset.get_sent_invoices(), test_data.sent_invoices)
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), test_data.sent_invoices)
        self.assertEqual(self.view.total_ctrl.GetValue(), '35,292.29')

        # verify sent invoice filter combo box loaded
        self.assertEqual(self.view.filter_ctrl.GetItems(), ['All', 'Grant Admin', 'Quarter'])
Beispiel #2
0
    def __init__(self):

        wx.Frame.__init__(self, None, title='allocat ledger', size=(1500, 820))
        self.SetBackgroundColour(gbl.COLOR_SCHEME.pnlBg)

        notebook = wx.Notebook(self)

        self.ledger_presenter = LedgerPresenter(notebook)
        self.import_presenter = ImportPresenter(notebook)
        self.data_mgt_presenter = DataMgtPresenter(notebook)

        self.config_notebook(notebook)
    def testLoadDetailsWithSalaryNoUserInput(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        with patch('lib.ui_lib.show_error') as mock_popup:
            mock_popup.return_value = None
            click_list_ctrl(self.view.work_list_ctrl, 5)

        # verify popup
        mock_popup.assert_not_called()

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': 3237.07,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 315')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'BANKS,ERNEST: 20% effort, from 01/20 thru 03/20 (91 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $44,444, Fringe: 4.4%, Total/Day: $177.86')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '3,237.07')
    def testLoadDetails_No_Salary(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        with patch('lib.ui_lib.show_error') as mock_popup:
            mock_popup.return_value = None
            click_list_ctrl(self.view.work_list_ctrl, 3)

        # verify popup
        mock_popup.assert_called_once_with('KALINE,ALBERT W has no salary! Not imported?')

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 309')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'KALINE,ALBERT W: 15% effort, from 02/20 thru 03/20 (60 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $0.00, Fringe: 0.0%, Total/Day: $0.00')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '')
    def testRunQueryWithAsnsNoLedgerRex(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q1_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 1)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global ledger data all built from billable assignments
        self.assertEqual(gbl.dataset.get_unsent_invoices(), test_data.invoices_q1_post_query)

        # verify display updated
        self.assertEqual(self.view.work_list_ctrl.GetObjects(), test_data.invoices_q1_post_query)

        # verify there is not list selection
        self.assertIsNone(self.view.get_work_list_selection())

        # verify details form is blank
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify the sent invoices are loaded
        self.assertEqual(gbl.dataset.get_sent_invoices(), test_data.invoices_q1_pre_query)
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), test_data.invoices_q1_pre_query)
        self.assertEqual(self.view.total_ctrl.GetValue(), '10,732.04')
    def testQueryNoAsnsNoLedger(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = []
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2019, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global unsent invoices is empty
        self.assertEqual(gbl.dataset.get_unsent_invoices(), [])

        # verify work list has no entries
        self.assertEqual(self.view.work_list_ctrl.GetObjects(), [])

        # verify there is not list selection
        self.assertIsNone(self.view.get_work_list_selection())

        # verify details form is blank
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify the sent invoices is empty
        self.assertEqual(gbl.dataset.get_sent_invoices(), [])
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), [])
        self.assertEqual(self.view.total_ctrl.GetValue(), '0.00')
class TestLedgerPresenter(unittest.TestCase):

    def setUp(self):
        self.app = wx.App()
        self.frame = wx.Frame(None)

        gbl.COLOR_SCHEME = gbl.SKINS[gbl.pick_scheme()]

    def tearDown(self):
        self.frame.Destroy()
        self.app.Destroy()

    def testInitView(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global dataset set populated
        self.assertEqual(gbl.dataset.get_emp_data(), test_data.employees)
        self.assertEqual(gbl.dataset.get_dept_data(), test_data.dept_objs)
        self.assertEqual(gbl.dataset.get_grant_admin_data(), test_data.grant_admin_objs)
        self.assertEqual(gbl.dataset.get_asn_data(), test_data.assignments)

        # verify dropdowns loaded
        self.assertEqual(self.view.dept_ctrl.GetItems(), test_data.department_items)
        self.assertEqual(self.view.grant_admin_ctrl.GetItems(), test_data.grant_admin_items)

        # verify the sent invoices are loaded
        self.assertEqual(gbl.dataset.get_sent_invoices(), test_data.sent_invoices)
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), test_data.sent_invoices)
        self.assertEqual(self.view.total_ctrl.GetValue(), '35,292.29')

        # verify sent invoice filter combo box loaded
        self.assertEqual(self.view.filter_ctrl.GetItems(), ['All', 'Grant Admin', 'Quarter'])

    def testQueryNoAsnsNoLedger(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = []
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2019, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global unsent invoices is empty
        self.assertEqual(gbl.dataset.get_unsent_invoices(), [])

        # verify work list has no entries
        self.assertEqual(self.view.work_list_ctrl.GetObjects(), [])

        # verify there is not list selection
        self.assertIsNone(self.view.get_work_list_selection())

        # verify details form is blank
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify the sent invoices is empty
        self.assertEqual(gbl.dataset.get_sent_invoices(), [])
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), [])
        self.assertEqual(self.view.total_ctrl.GetValue(), '0.00')

    def testRunQueryWithAsnsNoLedgerRex(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q1_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 1)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global ledger data all built from billable assignments
        self.assertEqual(gbl.dataset.get_unsent_invoices(), test_data.invoices_q1_post_query)

        # verify display updated
        self.assertEqual(self.view.work_list_ctrl.GetObjects(), test_data.invoices_q1_post_query)

        # verify there is not list selection
        self.assertIsNone(self.view.get_work_list_selection())

        # verify details form is blank
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify the sent invoices are loaded
        self.assertEqual(gbl.dataset.get_sent_invoices(), test_data.invoices_q1_pre_query)
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), test_data.invoices_q1_pre_query)
        self.assertEqual(self.view.total_ctrl.GetValue(), '10,732.04')

    def testRunQueryWithAsnsAndLedgerRex(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        # verify global ledger data all built from billable assignments
        self.assertEqual(gbl.dataset.get_unsent_invoices(), test_data.invoices_q2_post_query_unsent)

        # verify display updated
        self.assertEqual(self.view.work_list_ctrl.GetObjects(), test_data.invoices_q2_post_query_unsent)

        # verify there is not list selection
        self.assertIsNone(self.view.get_work_list_selection())

        # verify details form is blank
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify the sent invoices are loaded
        self.assertEqual(gbl.dataset.get_sent_invoices(), test_data.sent_invoices)
        self.assertEqual(self.view.done_list_ctrl.GetObjects(), test_data.sent_invoices)
        self.assertEqual(self.view.total_ctrl.GetValue(), '35,292.29')

    def testLoadDetails_No_Salary(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        with patch('lib.ui_lib.show_error') as mock_popup:
            mock_popup.return_value = None
            click_list_ctrl(self.view.work_list_ctrl, 3)

        # verify popup
        mock_popup.assert_called_once_with('KALINE,ALBERT W has no salary! Not imported?')

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': None,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 309')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'KALINE,ALBERT W: 15% effort, from 02/20 thru 03/20 (60 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $0.00, Fringe: 0.0%, Total/Day: $0.00')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '')

    def testLoadDetailsWithSalaryNoUserInput(self):
        with patch('models.invoice.Invoice.get_rex') as db_mock:
            db_mock.return_value = test_data.invoices_q2_pre_query
            gbl.dataset = LedgerDataSet(None)

        with patch('presenters.ledger_presenter.LedgerPresenter.get_this_quarter') as mock_qtr:
            mock_qtr.return_value = (2020, 2)
            self.presenter = LedgerPresenter(self.frame)
            self.view = self.presenter.view

        with patch('lib.ui_lib.show_error') as mock_popup:
            mock_popup.return_value = None
            click_list_ctrl(self.view.work_list_ctrl, 5)

        # verify popup
        mock_popup.assert_not_called()

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': 3237.07,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 315')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'BANKS,ERNEST: 20% effort, from 01/20 thru 03/20 (91 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $44,444, Fringe: 4.4%, Total/Day: $177.86')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '3,237.07')

    def testLoadDetailsWithSalaryAndUserInput(self):
        self.view.set_year(2020)
        self.view.set_qtr(1)

        click_button(self.view.qry_btn)

        with patch('lib.ui_lib.show_error') as mock_popup:
            mock_popup.return_value = None
            click_list_ctrl(self.view.work_list_ctrl, 0)

        # verify popup
        mock_popup.assert_not_called()

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': 'K0H1002',
            'amount': 4942.04,
            'short_code': '445566',
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 297')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'GEHRIG,HENRY LOUIS: 10% effort, from 10/19 thru 12/19 (92 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $104,971, Fringe: 33.5%, Total/Day: $537.18')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '4,942.04')

    def testAddEntryWithAmountChange(self):
        self.view.set_year(2020)
        self.view.set_qtr(1)

        self.presenter.run_query()

        # select an entry
        with patch('lib.ui_lib.show_error', return_value=None) as popup_mock:
            click_list_ctrl(self.view.work_list_ctrl, 4)

        # verify no salary popup not called
        popup_mock.assert_not_called()

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': None,
            'amount': 3272.65,
            'short_code': None,
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 315')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'BANKS,ERNEST: 20% effort, from 10/19 thru 12/19 (92 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $44,444, Fringe: 4.4%, Total/Day: $177.86')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '3,272.65')
        # self.assertEqual(self.view.paid_ctrl.GetValue(), False)
        # self.assertEqual(self.view.balance_ctrl.GetValue(), '3,272.65')

        # edit details
        enter_in_textbox_ctrl(self.view.invoice_ctrl, 'K0H0002')

        # change amount and simulate lost focus event
        enter_in_textbox_ctrl(self.view.amount_ctrl, '1000')
        self.presenter.update_amount_balance('1000')

        enter_in_textbox_ctrl(self.view.short_code_ctrl, '123456')
        click_combobox_ctrl(self.view.dept_ctrl, 2)
        check_checkbox_ctrl(self.view.admin_approved_ctrl)
        check_checkbox_ctrl(self.view.va_approved_ctrl)
        click_combobox_ctrl(self.view.grant_admin_ctrl, 3)

        # verify grant admin email is done
        self.assertEqual(self.view.get_grant_admin_email(), '*****@*****.**')

        with patch('dal.dao.Dao._Dao__write') as write_mock:
            write_mock.return_value = 4
            with patch('lib.ui_lib.show_msg') as popup_mock:
                popup_mock.return_value = None
                click_button(self.view.save_form_btn)

        # verify the DB call
        self.assertEqual(write_mock.call_count, 1)
        args, kwargs = write_mock.call_args
        self.assertEqual(len(args), 2)
        self.assertEqual(len(kwargs), 0)
        sql = ("INSERT INTO ledger "
               "(quarter,dept,admin_approved,va_approved,invoice_num,"
               "asn_id,project,employee,salary,fringe,total_day,frum,thru,"
               "effort,days,amount,paid,balance,short_code,"
               "grant_admin,grant_admin_email) "
               "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)")
        vals = ['20201', 'CARDIOLOGY', '1', '1', 'K0H0002',
                '2396', 'Prj 315', 'BANKS,ERNEST', '44444', '4.4',
                '177.86', '1910', '1912', '20', '92', '1000.0',
                '0', '1000.0', '123456', 'MARX,HARPO', '*****@*****.**'
        ]
        self.assertEqual(args[0], sql)
        self.assertEqual(args[1], vals)

        # verify user notification
        popup_mock.assert_called_once_with('Ledger updated!', 'Hooray')

        # verify list updated
        expected_item = Invoice({
            'admin_approved': True,
            'amount': 1000.0,
            'asn_id': 2396,
            'balance': 1000.0,
            'days': 92,
            'dept': 'CARDIOLOGY',
            'effort': 20,
            'employee': 'BANKS,ERNEST',
            'fringe': 4.4,
            'frum': '1910',
            'grant_admin': 'MARX,HARPO',
            'grant_admin_email': '*****@*****.**',
            'id': 4,
            'invoice_num': 'K0H0002',
            'paid': False,
            'project': 'Prj 315',
            'quarter': 20201,
            'salary': 44444,
            'short_code': '123456',
            'thru': '1912',
            'total_day': 177.86,
            'va_approved': True
        })
        self.assertEqual(self.view.get_work_list_selection(), expected_item)

    def testUpdateEntryWithAmountChange(self):
        self.view.set_year(2020)
        self.view.set_qtr(1)

        self.presenter.run_query()

        # select an entry
        with patch('lib.ui_lib.show_error', return_value=None) as popup_mock:
            click_list_ctrl(self.view.work_list_ctrl, 0)

        # verify no salary popup not called
        popup_mock.assert_not_called()

        # verify details
        expected = {
            'dept': None,
            'admin_approved': False,
            'va_approved': False,
            'invoice_num': 'K0H1002',
            'amount': 4942.04,
            'short_code': '445566',
            'grant_admin': None,
            'grant_admin_email': None
        }
        self.assertEqual(self.view.get_form_values(), expected)

        # verify display only items
        self.assertEqual(self.view.prj_lbl.GetLabelText(), 'Prj 297')
        self.assertEqual(self.view.asn_lbl.GetLabelText(),
                         'GEHRIG,HENRY LOUIS: 10% effort, from 10/19 thru 12/19 (92 days)')
        self.assertEqual(self.view.cost_lbl.GetLabelText(),
                         'Salary: $104,971, Fringe: 33.5%, Total/Day: $537.18')
        self.assertEqual(self.view.amount_ctrl.GetValue(), '4,942.04')

        # edit details
        click_combobox_ctrl(self.view.dept_ctrl, 2)
        check_checkbox_ctrl(self.view.admin_approved_ctrl)
        check_checkbox_ctrl(self.view.va_approved_ctrl)

        # change amount and simulate lost focus event
        enter_in_textbox_ctrl(self.view.amount_ctrl, '1000')
        self.presenter.update_amount_balance('1000')

        click_combobox_ctrl(self.view.grant_admin_ctrl, 3)

        # verify grant admin email is done
        self.assertEqual(self.view.get_grant_admin_email(), '*****@*****.**')

        with patch('dal.dao.Dao._Dao__write') as write_mock:
            write_mock.return_value = 1
            with patch('lib.ui_lib.show_msg') as popup_mock:
                popup_mock.return_value = None
                click_button(self.view.save_form_btn)

        # verify the DB call
        self.assertEqual(write_mock.call_count, 1)
        args, kwargs = write_mock.call_args
        self.assertEqual(len(args), 2)
        self.assertEqual(len(kwargs), 0)
        sql = ("UPDATE ledger "
               "SET dept=?,admin_approved=?,va_approved=?,invoice_num=?,"
               "amount=?,balance=?,short_code=?,"
               "grant_admin=?,grant_admin_email=?,sent=? "
               "WHERE id=?;")
        vals = [
            'CARDIOLOGY', '1', '1', 'K0H1002',
            '1000.0', '1000.0','445566',
            'MARX,HARPO', '*****@*****.**', '0', 2]
        self.assertEqual(args[0], sql)
        self.assertEqual(args[1], vals)

        # verify user notification
        popup_mock.assert_called_once_with('Ledger updated!', 'Hooray')

        # verify list updated
        expected_item = Invoice({
            'admin_approved': True,
            'amount': 1000.0,
            'asn_id': 2272,
            'balance': 1000.0,
            'days': 92,
            'dept': 'CARDIOLOGY',
            'effort': 10,
            'employee': 'GEHRIG,HENRY LOUIS',
            'fringe': 33.5,
            'frum': '1910',
            'grant_admin': 'MARX,HARPO',
            'grant_admin_email': '*****@*****.**',
            'id': 2,
            'invoice_num': 'K0H1002',
            'paid': False,
            'project': 'Prj 297',
            'quarter': 20201,
            'salary': 104971,
            'short_code': '445566',
            'thru': '1912',
            'total_day': 537.18,
            'va_approved': True,
            'sent': False
        })
        self.assertEqual(self.view.get_work_list_selection(), expected_item)

    def testGetTotalDays(self):
        from presenters.ledger_presenter import LedgerPresenter

        fxn = LedgerPresenter.get_total_days_asn

        # Quarter 1, 2020
        q_frum = '1910'
        q_thru = '1912'

        # First month only
        self.assertEqual(fxn(None, q_frum, q_thru, '1910', '1910'), 31)