stockor 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +2 -4
- data/Gemfile.lock +104 -87
- data/client/skr/api.js +3 -1
- data/client/skr/api/Components/AddressForm.cjsx +1 -1
- data/client/skr/api/Components/SingleItemCart.cjsx +1 -1
- data/client/skr/api/SingleItemCheckout.cjsx +3 -3
- data/client/skr/components/BankAccountFinder.cjsx +1 -1
- data/client/skr/components/CreditCardForm.cjsx +1 -1
- data/client/skr/components/Currency.cjsx +2 -1
- data/client/skr/components/CustomerFinder.cjsx +1 -1
- data/client/skr/components/CustomerProjectFinder.cjsx +1 -1
- data/client/skr/components/ExpenseCategoryFinder.cjsx +34 -0
- data/client/skr/components/GlAccountChooser.cjsx +2 -1
- data/client/skr/components/InvoiceFinder.cjsx +1 -1
- data/client/skr/components/LatexSnippets.cjsx +3 -0
- data/client/skr/components/LocationChooser.cjsx +3 -3
- data/client/skr/components/PaymentCategoryFinder.cjsx +1 -1
- data/client/skr/components/SalesOrderFinder.cjsx +1 -2
- data/client/skr/components/SkuFinder.cjsx +1 -1
- data/client/skr/components/SystemSettings.cjsx +4 -1
- data/client/skr/components/TermsChooser.cjsx +2 -2
- data/client/skr/components/ToolbarButton.cjsx +7 -3
- data/client/skr/components/TotalsLine.cjsx +5 -1
- data/client/skr/components/UserPreferences.cjsx +1 -1
- data/client/skr/components/VendorFinder.cjsx +1 -1
- data/client/skr/components/address/Address.cjsx +14 -1
- data/client/skr/components/styles.scss +6 -0
- data/client/skr/index.js +1 -0
- data/client/skr/lib/Remote.coffee +32 -0
- data/client/skr/lib/index.js +2 -0
- data/client/skr/lib/namespace.coffee +1 -0
- data/client/skr/models/CreditCardGateway.coffee +1 -1
- data/client/skr/models/ExpenseCategory.coffee +11 -0
- data/client/skr/models/ExpenseEntry.coffee +64 -0
- data/client/skr/models/ExpenseEntryCategory.coffee +11 -0
- data/client/skr/models/Invoice.coffee +4 -1
- data/client/skr/models/Payment.coffee +2 -2
- data/client/skr/models/SalesOrder.coffee +3 -2
- data/client/skr/models/SequentialId.coffee +1 -1
- data/client/skr/models/Vendor.coffee +1 -1
- data/client/skr/models/mixins/PrintSupport.coffee +1 -1
- data/client/skr/screens/bank-maint/BankMaint.cjsx +1 -1
- data/client/skr/screens/chart-of-accounts/ChartOfAccounts.cjsx +1 -1
- data/client/skr/screens/customer-maint/CustomerMaint.cjsx +1 -1
- data/client/skr/screens/customer-projects/CustomerProjects.cjsx +1 -1
- data/client/skr/screens/expense-categories/ExpenseCategories.cjsx +31 -0
- data/client/skr/screens/expense-categories/index.js +5 -0
- data/client/skr/screens/expense-categories/index.scss +9 -0
- data/client/skr/screens/expense-entry/ApprovalRequest.coffee +14 -0
- data/client/skr/screens/expense-entry/ApproveDialog.cjsx +30 -0
- data/client/skr/screens/expense-entry/Categories.cjsx +112 -0
- data/client/skr/screens/expense-entry/ExpenseEntry.cjsx +193 -0
- data/client/skr/screens/expense-entry/Form.cjsx +54 -0
- data/client/skr/screens/expense-entry/Grid.cjsx +51 -0
- data/client/skr/screens/expense-entry/ScreenState.coffee +1 -0
- data/client/skr/screens/expense-entry/index.js +1 -0
- data/client/skr/screens/expense-entry/index.scss +106 -0
- data/client/skr/screens/fresh-books-import/ApiInfo.cjsx +3 -3
- data/client/skr/screens/fresh-books-import/ChooseRecords.cjsx +6 -4
- data/client/skr/screens/fresh-books-import/FreshBooksImport.cjsx +1 -1
- data/client/skr/screens/fresh-books-import/ViewRecords.cjsx +2 -2
- data/client/skr/screens/fresh-books-import/index.scss +5 -4
- data/client/skr/screens/gl-accounts/GlAccounts.cjsx +1 -1
- data/client/skr/screens/gl-transactions/GlTransactions.cjsx +1 -1
- data/client/skr/screens/invoice/Invoice.cjsx +6 -2
- data/client/skr/screens/invoice/Payment.cjsx +1 -1
- data/client/skr/screens/invoice/TotalExtraInfo.cjsx +32 -0
- data/client/skr/screens/invoice/index.scss +4 -2
- data/client/skr/screens/locations/Locations.cjsx +1 -1
- data/client/skr/screens/payment-category/PaymentCategory.cjsx +1 -1
- data/client/skr/screens/payment-terms/PaymentTerms.cjsx +1 -1
- data/client/skr/screens/payments/Payments.cjsx +13 -6
- data/client/skr/screens/sale-report/SaleReport.cjsx +1 -1
- data/client/skr/screens/sales-order/SalesOrder.cjsx +2 -1
- data/client/skr/screens/sku-maint/SkuMaint.cjsx +1 -1
- data/client/skr/screens/sku-maint/SkuUomList.cjsx +2 -2
- data/client/skr/screens/time-invoicing/TimeInvoicing.cjsx +4 -4
- data/client/skr/screens/time-tracking/EditEntry.cjsx +2 -2
- data/client/skr/screens/time-tracking/Entries.coffee +6 -4
- data/client/skr/screens/time-tracking/Popover.cjsx +11 -6
- data/client/skr/screens/time-tracking/TimeTracking.cjsx +12 -5
- data/client/skr/screens/vendor-maint/VendorMaint.cjsx +2 -2
- data/config/database.yml +2 -0
- data/config/puma.rb +0 -4
- data/config/routes.rb +4 -0
- data/config/screens.rb +17 -0
- data/db/migrate/20140323001446_create_so_details_view.rb +2 -2
- data/db/migrate/20151121211323_create_customer_project_details_views.rb +1 -1
- data/db/migrate/20160216142845_create_gl_account_balances_view.rb +1 -1
- data/db/migrate/20160726004411_string_check_numbers.rb +5 -0
- data/db/migrate/20160805002717_create_expenses.rb +65 -0
- data/db/schema.sql +394 -229
- data/db/seed.rb +12 -12
- data/lib/skr/access_roles.rb +9 -4
- data/lib/skr/concerns/has_gl_transaction.rb +1 -1
- data/lib/skr/configuration.rb +3 -1
- data/lib/skr/db/migration_helpers.rb +2 -2
- data/lib/skr/handlers/approve_expense_entries.rb +16 -0
- data/lib/skr/handlers/invoice_from_time_entries.rb +1 -1
- data/lib/skr/handlers/sequential_ids.rb +2 -2
- data/lib/skr/jobs/fresh_books/import.rb +18 -17
- data/lib/skr/model.rb +3 -0
- data/lib/skr/models/bank_account.rb +9 -0
- data/lib/skr/models/expense_category.rb +27 -0
- data/lib/skr/models/expense_entry.rb +76 -0
- data/lib/skr/models/expense_entry_category.rb +15 -0
- data/lib/skr/models/gl_transaction.rb +2 -2
- data/lib/skr/models/inv_line.rb +3 -1
- data/lib/skr/models/invoice.rb +9 -5
- data/lib/skr/models/payment.rb +1 -1
- data/lib/skr/models/purchase_order.rb +4 -4
- data/lib/skr/models/uom.rb +2 -2
- data/lib/skr/models/voucher.rb +4 -4
- data/lib/skr/print/form.rb +17 -14
- data/lib/skr/version.rb +1 -1
- data/spec/fixtures/skr/expense_category.yml +17 -0
- data/spec/fixtures/skr/expense_entries.yml +20 -0
- data/spec/fixtures/skr/expense_entry_category.yml +19 -0
- data/spec/fixtures/skr/gl_account.yml +10 -0
- data/spec/server/handlers/invoice_from_time_entries_spec.rb +2 -2
- data/spec/server/handlers/sequential_ids_spec.rb +1 -1
- data/spec/server/models/expense_category_spec.rb +17 -0
- data/spec/server/models/expense_entry_spec.rb +44 -0
- data/spec/server/models/invoice_spec.rb +0 -1
- data/spec/server/models/spec_helper_spec.rb +5 -10
- data/spec/server/spec_helper.rb +12 -11
- data/spec/skr/components/SkuLinesSpec.coffee +11 -9
- data/spec/skr/models/CustomerSpec.coffee +1 -1
- data/spec/skr/models/ExpenseAccountSpec.coffee +5 -0
- data/spec/skr/screens/expense-categories/ExpenseCategoriesSpec.coffee +5 -0
- data/spec/skr/screens/expense-entry/ExpenseEntrySpec.coffee +5 -0
- data/spec/skr/screens/locations/LocationsSpec.coffee +4 -4
- data/spec/skr/screens/time-tracking/TimeTrackingSpec.coffee +4 -4
- data/stockor.gemspec +2 -3
- data/templates/print/partials/header.tex.erb +1 -1
- data/templates/print/partials/skus_table_invoice_footer.tex.erb +22 -19
- data/templates/print/partials/skus_table_invoice_info_line.tex.erb +5 -1
- data/templates/print/types/payment/default.tex.erb +10 -10
- metadata +40 -14
- data/client/skr/api/onReady.coffee +0 -23
- data/spec/skr/api/SingleItemCheckoutSpec.cjsx +0 -10
- data/spec/skr/screens/bank-maint/BankMaintSpec.coffee +0 -5
- data/spec/skr/screens/gl-accounts/GlAccountsSpec.coffee +0 -5
- data/spec/skr/screens/invoice/InvoiceSpec.coffee +0 -5
- data/spec/skr/screens/payment-category/PaymentCategorySpec.coffee +0 -5
- data/spec/skr/screens/payments/PaymentsSpec.coffee +0 -5
- data/spec/skr/screens/sale-report/SaleReportSpec.coffee +0 -5
@@ -14,7 +14,7 @@ class Skr.Models.Payment extends Skr.Models.Base
|
|
14
14
|
amount: {type:"bigdec", required: true, default: ->
|
15
15
|
_.bigDecimal('0')
|
16
16
|
}
|
17
|
-
check_number: {type:"
|
17
|
+
check_number: {type:"string"}
|
18
18
|
date: {type:"date", default: ->
|
19
19
|
new Date
|
20
20
|
}
|
@@ -51,6 +51,6 @@ class Skr.Models.Payment extends Skr.Models.Base
|
|
51
51
|
|
52
52
|
onSetVendor: ->
|
53
53
|
return unless @vendor and @vendor.isPersistent()
|
54
|
-
@name = @vendor.name
|
54
|
+
@name = @vendor.billing_address.name
|
55
55
|
@vendor.withAssociations(['billing_address']).then (v) =>
|
56
56
|
@address = v.billing_address.toString()
|
@@ -27,7 +27,10 @@ class Skr.Models.SalesOrder extends Skr.Models.Base
|
|
27
27
|
# optional attributes from details view
|
28
28
|
session:
|
29
29
|
customer_code: {type:"string"}
|
30
|
+
customer_name: {type:"string"}
|
31
|
+
bill_addr_name: {type:"string"}
|
30
32
|
order_total: {type:"bigdec"}
|
33
|
+
num_lines: {type:"integer"}
|
31
34
|
|
32
35
|
derived:
|
33
36
|
total: deps: ['order_total'], fn: ->
|
@@ -65,11 +68,9 @@ class Skr.Models.SalesOrder extends Skr.Models.Base
|
|
65
68
|
|
66
69
|
onCustomerChange: ->
|
67
70
|
return if @customer.isProxy or not @isNew()
|
68
|
-
|
69
71
|
associations = ['billing_address', 'shipping_address']
|
70
72
|
for attr in ['terms_id']
|
71
73
|
@set(attr, @customer[attr])
|
72
|
-
|
73
74
|
@customer.withAssociations(associations).then =>
|
74
75
|
for name in associations
|
75
76
|
@[name].set(
|
@@ -22,7 +22,7 @@ class Skr.Models.Vendor extends Skr.Models.Base
|
|
22
22
|
shipping_address: { model: "Address" }
|
23
23
|
terms: { model: "PaymentTerm", required: true }
|
24
24
|
gl_payables_account: { model: "GlAccount", required: true }
|
25
|
-
gl_freight_account: { model: "GlAccount"
|
25
|
+
gl_freight_account: { model: "GlAccount" }
|
26
26
|
purchase_orders: { collection: "PurchaseOrder" }
|
27
27
|
vouchers: { collection: "Voucher" }
|
28
28
|
vendor_skus: { collection: "SkuVendor" }
|
@@ -4,6 +4,6 @@ Skr.Models.Mixins.PrintSupport = {
|
|
4
4
|
if @printFormIdentifier then @printFormIdentifier() else @modelTypeIdentifier()
|
5
5
|
|
6
6
|
pdfDownloadUrl: ->
|
7
|
-
"#{Lanes.config.api_path}/skr/print/#{@_mixinPrintFormIdentifier()}/#{@hash_code}.pdf"
|
7
|
+
"//#{Lanes.config.api_host}#{Lanes.config.api_path}/skr/print/#{@_mixinPrintFormIdentifier()}/#{@hash_code}.pdf"
|
8
8
|
|
9
9
|
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Skr.Screens.ExpenseCategories extends Skr.Screens.Base
|
2
|
+
|
3
|
+
modelBindings:
|
4
|
+
category: ->
|
5
|
+
@loadOrCreateModel({
|
6
|
+
klass: Skr.Models.ExpenseCategory
|
7
|
+
prop: 'category', attribute: 'code'
|
8
|
+
})
|
9
|
+
|
10
|
+
getInitialState: ->
|
11
|
+
commands: new Skr.Screens.Commands(this, modelName: 'category')
|
12
|
+
|
13
|
+
render: ->
|
14
|
+
<LC.ScreenWrapper identifier="expense-categories">
|
15
|
+
<SC.ScreenControls commands={@state.commands} />
|
16
|
+
|
17
|
+
<BS.Row>
|
18
|
+
<SC.ExpenseCategoryFinder sm=2 autoFocus editOnly
|
19
|
+
commands={@state.commands} model={@category}
|
20
|
+
/>
|
21
|
+
|
22
|
+
<LC.Input sm=9 name='name' model={@category} />
|
23
|
+
</BS.Row>
|
24
|
+
<BS.Row>
|
25
|
+
<SC.GlAccountChooser sm=3 label="GL Account"
|
26
|
+
name="gl_account" model={@category} />
|
27
|
+
|
28
|
+
<LC.ToggleField model={@category} name='is_active' sm=2 />
|
29
|
+
</BS.Row>
|
30
|
+
|
31
|
+
</LC.ScreenWrapper>
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class Skr.Screens.ExpenseEntry.ApprovalRequest extends Lanes.Models.Base
|
2
|
+
|
3
|
+
props:
|
4
|
+
bank_account_id:{"type":"integer"}
|
5
|
+
expense_ids: { type: 'array', default: -> [] }
|
6
|
+
|
7
|
+
api_path: ->
|
8
|
+
'/skr/expense-entries/approve'
|
9
|
+
|
10
|
+
associations:
|
11
|
+
bank_account: { model: "BankAccount" }
|
12
|
+
|
13
|
+
|
14
|
+
isValid: -> !!@bank_account_id
|
@@ -0,0 +1,30 @@
|
|
1
|
+
class Skr.Screens.ExpenseEntry.Approve extends Lanes.React.Component
|
2
|
+
|
3
|
+
modelBindings:
|
4
|
+
approvalRequest: ->
|
5
|
+
new Skr.Screens.ExpenseEntry.ApprovalRequest
|
6
|
+
|
7
|
+
query: 'props'
|
8
|
+
|
9
|
+
approveSelected: ->
|
10
|
+
@approvalRequest.expense_ids = []
|
11
|
+
@query.results.eachRow (row, xd) =>
|
12
|
+
if LC.Grid.Selections.isSelected(row, xd)
|
13
|
+
@approvalRequest.expense_ids.push(@query.results.idForRow(row))
|
14
|
+
@approvalRequest.save().then @props.onApproved
|
15
|
+
|
16
|
+
render: ->
|
17
|
+
props = _.omit(@props, 'query', 'onApproved')
|
18
|
+
|
19
|
+
<BS.Popover
|
20
|
+
{...props}
|
21
|
+
id="choose-bank-account" title="Choose bank account">
|
22
|
+
<SC.BankAccountFinder
|
23
|
+
selectField fieldOnly editOnly
|
24
|
+
model={@approvalRequest} name="bank_account"
|
25
|
+
/>
|
26
|
+
<BS.Button
|
27
|
+
disabled={not @approvalRequest.isValid()}
|
28
|
+
bsStyle="success" onClick={@approveSelected}
|
29
|
+
>Approve</BS.Button>
|
30
|
+
</BS.Popover>
|
@@ -0,0 +1,112 @@
|
|
1
|
+
class TextCategory extends Lanes.React.Component
|
2
|
+
modelBindings:
|
3
|
+
category: 'props'
|
4
|
+
|
5
|
+
render: ->
|
6
|
+
code = @props.categories.get(@category.category_id).code
|
7
|
+
<span className='pair'>
|
8
|
+
{Lanes.u.format.currency(@category.amount)} {code}
|
9
|
+
</span>
|
10
|
+
|
11
|
+
|
12
|
+
class Category extends Lanes.React.Component
|
13
|
+
|
14
|
+
modelBindings:
|
15
|
+
category: 'props'
|
16
|
+
|
17
|
+
delete: ->
|
18
|
+
@props.onEntryDestroy(@props.category)
|
19
|
+
|
20
|
+
render: ->
|
21
|
+
<div className="category">
|
22
|
+
<SC.ExpenseCategoryFinder selectField
|
23
|
+
choices={@props.categories.models} fetchOnSelect={false}
|
24
|
+
model={@category} name='category' />
|
25
|
+
<LC.NumberInput name='amount' model={@category} align="right" />
|
26
|
+
<BS.Button className="delete"
|
27
|
+
onClick={@delete}
|
28
|
+
>
|
29
|
+
<LC.Icon type="trash-o" lg />
|
30
|
+
</BS.Button>
|
31
|
+
|
32
|
+
</div>
|
33
|
+
|
34
|
+
class Skr.Screens.ExpenseEntry.Categories extends Lanes.React.Component
|
35
|
+
|
36
|
+
modelBindings:
|
37
|
+
entry: 'props'
|
38
|
+
categories: 'props'
|
39
|
+
entryCategories: ->
|
40
|
+
if @props.entry.categories.isEmpty()
|
41
|
+
@props.entry.categories.add({})
|
42
|
+
@props.entry.categories
|
43
|
+
|
44
|
+
addCategory: ->
|
45
|
+
@entry.categories.add({})
|
46
|
+
@setState(overlayShown: true)
|
47
|
+
_.defer =>
|
48
|
+
_.dom(@, '.popover-content .category:last-child .select input').focus()
|
49
|
+
|
50
|
+
onMultiClick: ->
|
51
|
+
@setState(overlayShown: true)
|
52
|
+
|
53
|
+
getPopOverTarget: ->
|
54
|
+
_.dom(@refs.overlayTarget).el
|
55
|
+
|
56
|
+
onEntryDestroy: (cat) ->
|
57
|
+
@setState(overlayShown: false)
|
58
|
+
@props.entry.categories.remove(cat)
|
59
|
+
|
60
|
+
renderMultiple: ->
|
61
|
+
|
62
|
+
entries = @entry.categories.map (category) =>
|
63
|
+
<Category key={category.cid} category={category}
|
64
|
+
onEntryDestroy={@onEntryDestroy}
|
65
|
+
categories={@props.categories} />
|
66
|
+
msg =
|
67
|
+
@entry.categories.map (expcat) =>
|
68
|
+
category = @categories.get( expcat.category_id )
|
69
|
+
return null unless category
|
70
|
+
<TextCategory key={expcat.cid} category={expcat} categories={@categories} />
|
71
|
+
|
72
|
+
<BS.Col sm=12 className="multiple">
|
73
|
+
<BS.FormGroup>
|
74
|
+
<BS.ControlLabel>Categories</BS.ControlLabel>
|
75
|
+
<div className='controls'>
|
76
|
+
<BS.FormControl.Static onClick={@onMultiClick}>{msg}</BS.FormControl.Static>
|
77
|
+
<LC.IconButton icon="edit" className="view" ref="overlayTarget"
|
78
|
+
onClick={=> @setState(overlayShown: true)}
|
79
|
+
/>
|
80
|
+
<BS.Overlay
|
81
|
+
show={@state.overlayShown}
|
82
|
+
rootClose=true
|
83
|
+
onHide={=> @setState(overlayShown: false)}
|
84
|
+
placement="bottom"
|
85
|
+
container={@}
|
86
|
+
target={@getPopOverTarget}
|
87
|
+
>
|
88
|
+
<BS.Popover id="popover-positioned-bottom" title="Entries">
|
89
|
+
{entries}
|
90
|
+
</BS.Popover>
|
91
|
+
</BS.Overlay>
|
92
|
+
</div>
|
93
|
+
</BS.FormGroup>
|
94
|
+
</BS.Col>
|
95
|
+
|
96
|
+
render: ->
|
97
|
+
|
98
|
+
body = switch @entry.categories.length
|
99
|
+
when 1
|
100
|
+
<Category category={@entry.categories.first()}
|
101
|
+
categories={@props.categories} />
|
102
|
+
else
|
103
|
+
@renderMultiple()
|
104
|
+
|
105
|
+
props = _.without( @props, _.keys(@constructor.propTypes) )
|
106
|
+
|
107
|
+
<BS.Col {...props} className="categories">
|
108
|
+
{body}
|
109
|
+
<BS.FormGroup className='add'>
|
110
|
+
<LC.IconButton icon="plus" onClick={@addCategory} />
|
111
|
+
</BS.FormGroup>
|
112
|
+
</BS.Col>
|
@@ -0,0 +1,193 @@
|
|
1
|
+
##= require_self
|
2
|
+
##= require ./Form
|
3
|
+
##= require ./Grid
|
4
|
+
##= require ./Categories
|
5
|
+
##= require ./ApprovalRequest
|
6
|
+
##= require ./ApproveDialog
|
7
|
+
|
8
|
+
isApproved = (row) ->
|
9
|
+
!!row[2]
|
10
|
+
|
11
|
+
getCategoryVal = (type, fields, q) ->
|
12
|
+
list = fields[1]
|
13
|
+
if _.isArray(q.syncOptions.with)
|
14
|
+
_.reduce(list, (amt, cat) ->
|
15
|
+
amt + cat[type]
|
16
|
+
, 0)
|
17
|
+
else
|
18
|
+
category_id = q.syncOptions.with.with_category_details
|
19
|
+
cat = _.find(list , (f) ->
|
20
|
+
f.category_id = category_id
|
21
|
+
)
|
22
|
+
cat?[type] || 0.0
|
23
|
+
|
24
|
+
BALANCE_FIELD = {
|
25
|
+
id: 'balance', title: 'Balance',
|
26
|
+
fixedWidth: 100, textAlign: 'right', query: false,
|
27
|
+
format: (e, fields, q) ->
|
28
|
+
Lanes.u.format.currency getCategoryVal('balance', fields, q)
|
29
|
+
}
|
30
|
+
|
31
|
+
class Skr.Screens.ExpenseEntry extends Skr.Screens.Base
|
32
|
+
|
33
|
+
getInitialState: ->
|
34
|
+
isReviewing: false
|
35
|
+
|
36
|
+
syncOptions:
|
37
|
+
include: ['categories', 'attachments' ]
|
38
|
+
|
39
|
+
modelBindings:
|
40
|
+
categories: ->
|
41
|
+
Skr.Models.ExpenseCategory.Collection.fetch()
|
42
|
+
|
43
|
+
entry: ->
|
44
|
+
@loadOrCreateModel({
|
45
|
+
syncOptions: @syncOptions, klass: Skr.Models.ExpenseEntry
|
46
|
+
prop: 'entry', attribute: 'id'
|
47
|
+
})
|
48
|
+
|
49
|
+
query: ->
|
50
|
+
new Lanes.Models.Query({
|
51
|
+
autoRetrieve: true, sortAscending: false,
|
52
|
+
src: Skr.Models.ExpenseEntry,
|
53
|
+
syncOptions: _.extend({}, @syncOptions,
|
54
|
+
with: ['with_category_details']
|
55
|
+
),
|
56
|
+
fields: [
|
57
|
+
{ id: 'id', visible: false }
|
58
|
+
{ id: 'category_list', visible: false }
|
59
|
+
{ id: 'gl_transaction_ids', visible: false }
|
60
|
+
{ id: 'occured', fixedWidth: 200, textAlign: 'center', format: Lanes.u.format.dateTime }
|
61
|
+
{ id: 'name', flex: 1 }
|
62
|
+
{
|
63
|
+
id: 'amount', fixedWidth: 100, textAlign: 'right', query: false,
|
64
|
+
format: (e, fields, q) ->
|
65
|
+
Lanes.u.format.currency getCategoryVal('amount', fields, q)
|
66
|
+
}
|
67
|
+
]
|
68
|
+
})
|
69
|
+
|
70
|
+
getSelectionAttributes: ({row}) ->
|
71
|
+
if isApproved(row) then {checked: true, disabled: true} else {}
|
72
|
+
|
73
|
+
setReviewState: (@isReviewing) ->
|
74
|
+
@setState({@isReviewing})
|
75
|
+
@query.syncOptions.with = ['with_category_details']
|
76
|
+
if @isReviewing
|
77
|
+
@query.syncOptions.queryParams = {review: true}
|
78
|
+
@query.syncOptions.with.push('with_user_logins')
|
79
|
+
@query.fields.add({ id: 'created_by_user.login', title: 'User', fixedWidth: 120 }, at: 3)
|
80
|
+
@gridSelections = new LC.Grid.Selections(
|
81
|
+
onChange: @rowSelectChanged, attributesGetter: @getSelectionAttributes
|
82
|
+
)
|
83
|
+
@query.fields.add(@gridSelections, at: 2)
|
84
|
+
else
|
85
|
+
delete @query.syncOptions.queryParams
|
86
|
+
delete @gridSelections
|
87
|
+
@query.fields.remove(['selected', 'created_by_user.login'])
|
88
|
+
|
89
|
+
@query.results.reload()
|
90
|
+
|
91
|
+
rowSelectChanged: ->
|
92
|
+
@forceUpdate()
|
93
|
+
|
94
|
+
onEntrySelect: (entry, index) ->
|
95
|
+
entry.fetch(@syncOptions).then (e) =>
|
96
|
+
@modelBindings.reset({entry})
|
97
|
+
@query.markModified()
|
98
|
+
@refs.form.focus()
|
99
|
+
|
100
|
+
onEntryReset: ->
|
101
|
+
entry = new Skr.Models.ExpenseEntry
|
102
|
+
entry.categories.add({})
|
103
|
+
@modelBindings.reset({entry})
|
104
|
+
@refs.form.focus()
|
105
|
+
|
106
|
+
onCategoryChange: ->
|
107
|
+
@query.results.reload()
|
108
|
+
|
109
|
+
onEntrySave: ->
|
110
|
+
isNew = @entry.isNew()
|
111
|
+
isCategoriesChanged = @entry.categories.isDirty()
|
112
|
+
@entry.save().then =>
|
113
|
+
return if @entry.hasErrors
|
114
|
+
if isNew or isCategoriesChanged
|
115
|
+
@query.results.reload()
|
116
|
+
else
|
117
|
+
@query.results.saveModelChanges(@entry)
|
118
|
+
@onEntryReset()
|
119
|
+
|
120
|
+
onConfirmationToggle: (selected) ->
|
121
|
+
@query.results.eachRow (row, xd) ->
|
122
|
+
LC.Grid.Selections.setRow(row, xd, selected)
|
123
|
+
@query.markModified()
|
124
|
+
|
125
|
+
onApproved: ->
|
126
|
+
@refs.overlay.hide()
|
127
|
+
@query.results.reload()
|
128
|
+
|
129
|
+
selectedState: ->
|
130
|
+
none = all = true
|
131
|
+
@query.results.eachRow (row, xd) ->
|
132
|
+
if LC.Grid.Selections.isSelected(row, xd) and not isApproved(row)
|
133
|
+
none = false
|
134
|
+
else
|
135
|
+
all = false
|
136
|
+
if none is false and all is false
|
137
|
+
return 'break'
|
138
|
+
if all then true else if none then false else null
|
139
|
+
|
140
|
+
onCategorySelect: (category) ->
|
141
|
+
if category
|
142
|
+
@query.syncOptions.with = { with_category_details: category.id }
|
143
|
+
@query.fields.add(BALANCE_FIELD)
|
144
|
+
else
|
145
|
+
@query.fields.remove(BALANCE_FIELD.id)
|
146
|
+
category = new Skr.Models.ExpenseCategory
|
147
|
+
@query.syncOptions.with = [ 'with_category_details' ]
|
148
|
+
@setState({category})
|
149
|
+
@query.results.reload()
|
150
|
+
|
151
|
+
isRowEditing: (row) ->
|
152
|
+
@entry.id is row[0]
|
153
|
+
|
154
|
+
render: ->
|
155
|
+
selectionState = @selectedState()
|
156
|
+
<LC.ScreenWrapper flexVertical
|
157
|
+
identifier="expense-entry"
|
158
|
+
className={_.classnames('is-reviewing': @isReviewing)}
|
159
|
+
>
|
160
|
+
|
161
|
+
<Skr.Screens.ExpenseEntry.Form
|
162
|
+
ref="form"
|
163
|
+
categories={@categories}
|
164
|
+
entry={@entry}
|
165
|
+
onEntryReset={@onEntryReset}
|
166
|
+
onEntrySave={@onEntrySave}
|
167
|
+
/>
|
168
|
+
|
169
|
+
<Skr.Screens.ExpenseEntry.Grid
|
170
|
+
onCategorySelect={@onCategorySelect}
|
171
|
+
setReviewState={@setReviewState}
|
172
|
+
isReviewing={@isReviewing}
|
173
|
+
isRowFocused={@isRowEditing}
|
174
|
+
isConfirmationChecked={selectionState}
|
175
|
+
categories={@categories}
|
176
|
+
onRecordSelect={@onEntrySelect}
|
177
|
+
onConfirmationToggle={@onConfirmationToggle}
|
178
|
+
query={@query}
|
179
|
+
/>
|
180
|
+
|
181
|
+
<BS.Row className='footer'>
|
182
|
+
<BS.Col sm=12>
|
183
|
+
<BS.OverlayTrigger
|
184
|
+
rootClose={true} container={@} trigger="click" ref="overlay" placement="top"
|
185
|
+
overlay={<Skr.Screens.ExpenseEntry.Approve
|
186
|
+
query={@query} onApproved={@onApproved}/>}
|
187
|
+
>
|
188
|
+
<BS.Button bsStyle="primary" disabled={selectionState is false}>Approve</BS.Button>
|
189
|
+
</BS.OverlayTrigger>
|
190
|
+
</BS.Col>
|
191
|
+
</BS.Row>
|
192
|
+
|
193
|
+
</LC.ScreenWrapper>
|