-
-
Save stravid/0da9bd2e96b485eff0a4 to your computer and use it in GitHub Desktop.
Invoice Push Update Application
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'sinatra' | |
require 'json' | |
current_invoice_id = 0 | |
invoices = {} | |
before do | |
content_type :json | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Invoice drafts are grouped and correctly displayed', function() { | |
visit('/'); | |
andThen(function() { | |
equal(currentPath(), 'index'); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import startApp from '../helpers/start-app'; | |
var dueAt = new Date(); | |
dueAt.setTime(new Date().getTime() + 8 * 24 * 60 * 60 * 1000 + 60000); | |
var billedAt = new Date(); | |
billedAt.setTime(new Date().getTime() - 1 * 24 * 60 * 60 * 1000 + 1000); | |
var paidAt = new Date(2014, 7, 12, 10); | |
var INVOICES = { | |
"1": { | |
id: 1, | |
title: 'Draft 1', | |
client: 'Company A', | |
description: '', | |
amount: 420, | |
status: 'draft', | |
due_at: null, | |
billed_at: null, | |
paid_at: null | |
}, | |
"2": { | |
id: 2, | |
title: 'Draft 2', | |
client: 'Company B', | |
description: '', | |
amount: 4200, | |
status: 'draft', | |
due_at: null, | |
billed_at: null, | |
paid_at: null | |
}, | |
"3": { | |
id: 3, | |
title: 'Open', | |
client: 'Company C', | |
description: '', | |
amount: 42200, | |
status: 'open', | |
due_at: dueAt, | |
billed_at: billedAt, | |
paid_at: null | |
}, | |
"4": { | |
id: 4, | |
title: 'Paid', | |
client: 'Company D', | |
description: '', | |
amount: 422200, | |
status: 'paid', | |
due_at: dueAt, | |
billed_at: billedAt, | |
paid_at: paidAt | |
} | |
}; | |
var App; | |
var server; | |
module('Acceptance: InvoiceOverview', { | |
setup: function() { | |
App = startApp(); | |
server = new Pretender(function() { | |
this.get('/invoices', function(request) { | |
var result = JSON.stringify({ invoices: Object.keys(INVOICES).map(function(k) { return INVOICES[k]; }) }); | |
return [200, { 'Content-Type': 'application/json' }, result]; | |
}); | |
}); | |
}, | |
teardown: function() { | |
Ember.run(App, 'destroy'); | |
server.shutdown(); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Invoice drafts are grouped and correctly displayed', function() { | |
visit('/'); | |
andThen(function() { | |
equal(find('table.drafts tr:eq(1) td:eq(0)').text(), 'Draft 1'); | |
equal(find('table.drafts tr:eq(1) td:eq(1)').text(), 'Company A'); | |
equal(find('table.drafts tr:eq(1) td:eq(2)').text(), '€ 4,20'); | |
equal(find('table.drafts tr:eq(2) td:eq(0)').text(), 'Draft 2'); | |
equal(find('table.drafts tr:eq(2) td:eq(1)').text(), 'Company B'); | |
equal(find('table.drafts tr:eq(2) td:eq(2)').text(), '€ 42,00'); | |
equal(find('table.heading:eq(0) th:eq(1)').text(), '€ 46,20'); | |
equal(find('table.drafts tr').length, 3); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
model: function() { | |
return this.store.find('invoice'); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import DS from 'ember-data'; | |
export default DS.ActiveModelAdapter.extend({ | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import DS from 'ember-data'; | |
export default DS.Model.extend({ | |
title: DS.attr('string'), | |
client: DS.attr('string'), | |
description: DS.attr('string'), | |
status: DS.attr('string'), | |
amount: DS.attr('number'), | |
dueAt: DS.attr('date'), | |
billedAt: DS.attr('date'), | |
paidAt: DS.attr('date') | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.ArrayController.extend({ | |
invoiceDrafts: function() { | |
return this.get('model').filterBy('status', 'draft'); | |
}.property('model.@each.status'), | |
invoiceDraftAmounts: Ember.computed.mapBy('invoiceDrafts', 'amount'), | |
totalAmountForInvoiceDrafts: Ember.computed.sum('invoiceDraftAmounts') | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { numberToCurrency } from '../../../helpers/number-to-currency'; | |
module('number-to-currency Handlebars Helper'); | |
test('convert invalid input to zero', function() { | |
equal(numberToCurrency(null), '€ 0,00'); | |
equal(numberToCurrency(undefined), '€ 0,00'); | |
}); | |
test('handle single digit input', function() { | |
equal(numberToCurrency(0), '€ 0,00'); | |
equal(numberToCurrency(9), '€ 0,09'); | |
}); | |
test('handle two digit input', function() { | |
equal(numberToCurrency(99), '€ 0,99'); | |
}); | |
test('handle multi digit input', function() { | |
equal(numberToCurrency(199), '€ 1,99'); | |
equal(numberToCurrency(10099), '€ 100,99'); | |
equal(numberToCurrency(100099), '€ 1.000,99'); | |
equal(numberToCurrency(100000099), '€ 1.000.000,99'); | |
}); | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
function numberToCurrency(value) { | |
if (value === null) { | |
value = 0; | |
} | |
if (isNaN(value)) { | |
value = 0; | |
} | |
value = value.toString(); | |
while (value.length < 3) { | |
value = '0' + value; | |
} | |
var fractionalPart = value.slice(-2); | |
var integerPart = value.slice(0, -2).replace(/\B(?=(\d{3})+(?!\d))/g, '.'); | |
return '€ ' + integerPart + ',' + fractionalPart; | |
} | |
export { numberToCurrency }; | |
export default Ember.Handlebars.makeBoundHelper(numberToCurrency); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
post '/invoices' do | |
params = JSON.parse request.body.read, symbolize_names: true | |
current_invoice_id += 1 | |
new_invoice = params[:invoice].merge!({ id: current_invoice_id }) | |
invoices[current_invoice_id] = new_invoice | |
{ invoice: new_invoice }.to_json | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Open invoices are grouped and correctly displayed', function() { | |
visit('/'); | |
andThen(function() { | |
equal(find('table.open-invoices tr:eq(1) td:eq(0)').text(), 'Open'); | |
equal(find('table.open-invoices tr:eq(1) td:eq(1)').text(), 'Company C'); | |
equal(find('table.open-invoices tr:eq(1) td:eq(2)').text().trim(), 'Due in 8 days'); | |
equal(find('table.open-invoices tr:eq(1) td:eq(3)').text(), '€ 422,00'); | |
equal(find('table.heading:eq(1) th:eq(1)').text(), '€ 422,00'); | |
equal(find('table.open-invoices tr').length, 2); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.ArrayController.extend({ | |
invoiceDrafts: function() { | |
return this.get('model').filterBy('status', 'draft'); | |
}.property('model.@each.status'), | |
invoiceDraftAmounts: Ember.computed.mapBy('invoiceDrafts', 'amount'), | |
totalAmountForInvoiceDrafts: Ember.computed.sum('invoiceDraftAmounts'), | |
openInvoices: function() { | |
return this.get('model').filterBy('status', 'open'); | |
}.property('model.@each.status'), | |
openInvoiceAmounts: Ember.computed.mapBy('openInvoices', 'amount'), | |
totalAmountForOpenInvoices: Ember.computed.sum('openInvoiceAmounts') | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import { test, moduleForComponent } from 'ember-qunit'; | |
moduleForComponent('due-at-label'); | |
var dayBasis = 24 * 60 * 60 * 1000; | |
function setDueAtToDaysFromNow(component, days) { | |
Ember.run(function() { | |
var dueAt = new Date(); | |
dueAt.setTime(new Date().getTime() + days + 60000); | |
component.set('dueAt', dueAt); | |
}); | |
} | |
test('displays correct text', function() { | |
var component = this.subject(); | |
setDueAtToDaysFromNow(component, 8 * dayBasis); | |
equal(this.$().text().trim(), 'Due in 8 days'); | |
setDueAtToDaysFromNow(component, 1 * dayBasis); | |
equal(this.$().text().trim(), 'Due in 1 day'); | |
setDueAtToDaysFromNow(component, 0 * dayBasis); | |
equal(this.$().text().trim(), 'Due today'); | |
setDueAtToDaysFromNow(component, -1 * dayBasis); | |
equal(this.$().text().trim(), '1 day overdue'); | |
setDueAtToDaysFromNow(component, -8 * dayBasis); | |
equal(this.$().text().trim(), '8 days overdue'); | |
}); | |
test('is a span tag', function() { | |
this.subject().set('dueAt', new Date()); | |
equal('SPAN', this.$().prop('tagName')); | |
}); | |
test('has correct class', function() { | |
var component = this.subject(); | |
setDueAtToDaysFromNow(component, 8 * dayBasis); | |
ok(this.$().hasClass('ok')); | |
setDueAtToDaysFromNow(component, 7 * dayBasis); | |
ok(this.$().hasClass('warning')); | |
setDueAtToDaysFromNow(component, 0 * dayBasis); | |
ok(this.$().hasClass('overdue')); | |
setDueAtToDaysFromNow(component, -1 * dayBasis); | |
ok(this.$().hasClass('overdue')); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Component.extend({ | |
tagName: 'span', | |
classNameBindings: [':due-label', 'isOverdue:overdue', 'isComingDue:warning', 'isLaterDue:ok'], | |
daysUntilDueDate: function() { | |
var difference = this.get('dueAt').getTime() - new Date().getTime(); | |
return Math.floor(difference / (24 * 60 * 60 * 1000)); | |
}.property('dueAt'), | |
isOverdue: function() { | |
return this.get('daysUntilDueDate') <= 0; | |
}.property('daysUntilDueDate'), | |
isComingDue: function() { | |
return this.get('daysUntilDueDate') > 0 && this.get('daysUntilDueDate') <= 7; | |
}.property('daysUntilDueDate'), | |
isLaterDue: function() { | |
return this.get('daysUntilDueDate') > 7; | |
}.property('daysUntilDueDate'), | |
label: function() { | |
var daysUntilDueDate = this.get('daysUntilDueDate'); | |
var dayPart = Math.abs(daysUntilDueDate) > 1 ? 'days' : 'day'; | |
if (daysUntilDueDate > 0) { | |
return 'Due in ' + daysUntilDueDate + ' ' + dayPart; | |
} else if (daysUntilDueDate === 0) { | |
return 'Due today'; | |
} else { | |
return Math.abs(daysUntilDueDate) + ' ' + dayPart + ' overdue'; | |
} | |
}.property('daysUntilDueDate') | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Paid invoices are grouped and correctly displayed', function() { | |
visit('/'); | |
andThen(function() { | |
equal(find('table.paid-invoices tr:eq(1) td:eq(0)').text(), 'Paid'); | |
equal(find('table.paid-invoices tr:eq(1) td:eq(1)').text(), 'Company D'); | |
equal(find('table.paid-invoices tr:eq(1) td:eq(2)').text(), '12.08.2014'); | |
equal(find('table.paid-invoices tr:eq(1) td:eq(3)').text(), '€ 4.222,00'); | |
equal(find('table.heading:eq(2) th:eq(1)').text(), '€ 4.222,00'); | |
equal(find('table.paid-invoices tr').length, 2); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.ArrayController.extend({ | |
invoiceDrafts: function() { | |
return this.get('model').filterBy('status', 'draft'); | |
}.property('model.@each.status'), | |
invoiceDraftAmounts: Ember.computed.mapBy('invoiceDrafts', 'amount'), | |
totalAmountForInvoiceDrafts: Ember.computed.sum('invoiceDraftAmounts'), | |
openInvoices: function() { | |
return this.get('model').filterBy('status', 'open'); | |
}.property('model.@each.status'), | |
openInvoiceAmounts: Ember.computed.mapBy('openInvoices', 'amount'), | |
totalAmountForOpenInvoices: Ember.computed.sum('openInvoiceAmounts'), | |
paidInvoices: function() { | |
return this.get('model').filterBy('status', 'paid'); | |
}.property('model.@each.status'), | |
paidInvoiceAmounts: Ember.computed.mapBy('paidInvoices', 'amount'), | |
totalAmountForPaidInvoices: Ember.computed.sum('paidInvoiceAmounts') | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As a small business owner | |
I want to create new invoice drafts | |
So that future me will not forget to bill a client |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
get '/invoices' do | |
{ invoices: invoices.map { |key, value| value }}.to_json | |
end | |
get '/invoices/:id' do |id| | |
{ invoice: invoices[id.to_i] }.to_json | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import startApp from '../helpers/start-app'; | |
var INVOICES = {}; | |
var CURRENT_INVOICE_ID = 1; | |
var App; | |
var server; | |
module('Acceptance: NewInvoiceDraft', { | |
setup: function() { | |
App = startApp(); | |
server = new Pretender(function() { | |
this.get('/invoices', function(request) { | |
var result = JSON.stringify({ invoices: Object.keys(INVOICES).map(function(k) { return INVOICES[k]; }) }); | |
return [200, { 'Content-Type': 'application/json' }, result]; | |
}); | |
this.post('/invoices', function(request) { | |
var invoice = JSON.parse(request.requestBody).invoice; | |
invoice.id = CURRENT_INVOICE_ID; | |
INVOICES[CURRENT_INVOICE_ID] = invoice; | |
var result = JSON.stringify({ invoice: invoice }); | |
CURRENT_INVOICE_ID++; | |
return [200, { 'Content-Type': 'application/json' }, result]; | |
}); | |
}); | |
}, | |
teardown: function() { | |
Ember.run(App, 'destroy'); | |
server.shutdown(); | |
} | |
}); | |
test('Create a new invoice draft', function() { | |
visit('/'); | |
click('.heading-inline-link'); | |
fillIn('#title', 'Writing Ember.js Learning Material'); | |
fillIn('#client', 'Awesome Inc.'); | |
fillIn('#amount', 4000); | |
fillIn('#description', 'A multi part article series detailing how to build an Ember.js Application with Push Updates'); | |
andThen(function() { | |
equal(find('h2').text(), 'Writing Ember.js Learning Material'); | |
click('#save-button'); | |
andThen(function() { | |
equal(currentRouteName(), 'index'); | |
equal(find('table.drafts tr:eq(1) td:eq(0)').text(), 'Writing Ember.js Learning Material'); | |
equal(find('table.drafts tr:eq(1) td:eq(2)').text(), '€ 4.000,00'); | |
}); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Router.map(function() { | |
this.route('new'); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
actions: { | |
save: function() { | |
this.modelFor('new').save(); | |
this.transitionTo('index'); | |
} | |
}, | |
model: function() { | |
return this.store.createRecord('invoice'); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { test, moduleForComponent } from 'ember-qunit'; | |
import Ember from 'ember'; | |
moduleForComponent('currency-input'); | |
test('passes ID to input', function() { | |
var component = this.subject(); | |
Ember.run(function() { | |
component.set('inputId', 'test-id'); | |
}); | |
equal(this.$().find('input').attr('id'), 'test-id'); | |
}); | |
test('displays nothing as default value', function() { | |
equal(this.$().find('input').val(), ''); | |
}); | |
test('returns undefined as default value', function() { | |
equal(this.subject().get('value'), undefined); | |
}); | |
test('displays formatted value in input', function() { | |
var component = this.subject(); | |
Ember.run(function() { | |
component.set('value', 420000); | |
}); | |
equal(this.$().find('input').val(), '4200'); | |
Ember.run(function(){ | |
component.set('value', 420); | |
}); | |
equal(this.$().find('input').val(), '4,2'); | |
}); | |
test('converts euro input to cents', function() { | |
var component = this.subject(); | |
var that = this; | |
Ember.run(function() { | |
that.$().find('input').val(100); | |
that.$().find('input').trigger('keyup'); | |
}); | |
equal(component.get('value'), 10000); | |
Ember.run(function() { | |
that.$().find('input').val('100,33'); | |
that.$().find('input').trigger('keyup'); | |
}); | |
equal(component.get('value'), 10033); | |
Ember.run(function() { | |
that.$().find('input').val('4.2'); | |
that.$().find('input').trigger('keyup'); | |
}); | |
equal(component.get('value'), 420); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Component.extend({ | |
inputValue: function(key, value) { | |
if (arguments.length > 1) { | |
this.set('value', parseFloat(value.toString().replace(',', '.')) * 100); | |
} | |
var displayValue = this.get('value') / 100; | |
if (isNaN(displayValue)) { | |
return ''; | |
} else { | |
return displayValue.toString().replace('.', ','); | |
} | |
}.property('value') | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
test('Discard an unsaved invoice draft', function() { | |
visit('/'); | |
click('.heading-inline-link'); | |
fillIn('#title', 'Write an AngularJS book'); | |
click('.destructive-link'); | |
andThen(function() { | |
equal(currentRouteName(), 'index'); | |
equal(find('table.drafts').text().indexOf('Write an AngularJS book'), -1); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
actions: { | |
save: function() { | |
this.modelFor('new').save(); | |
this.transitionTo('index'); | |
}, | |
discard: function() { | |
this.modelFor('new').deleteRecord(); | |
this.transitionTo('index'); | |
} | |
}, | |
model: function() { | |
return this.store.createRecord('invoice'); | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
import startApp from '../helpers/start-app'; | |
var INVOICES = { | |
"1": { | |
id: 1, | |
title: 'Draft 1', | |
client: 'Company A', | |
description: '', | |
amount: 420, | |
status: 'draft', | |
due_at: null, | |
billed_at: null, | |
paid_at: null | |
} | |
}; | |
var App; | |
var server; | |
module('Acceptance: EditInvoiceDraft', { | |
setup: function() { | |
App = startApp(); | |
server = new Pretender(function() { | |
this.get('/invoices', function(request) { | |
var result = JSON.stringify({ invoices: Object.keys(INVOICES).map(function(k) { return INVOICES[k]; }) }); | |
return [200, { 'Content-Type': 'application/json' }, result]; | |
}); | |
this.put('/invoices/1', function(request) { | |
var invoiceAttributes = JSON.parse(request.requestBody).invoice; | |
INVOICES['1'].title = invoiceAttributes.title; | |
var result = JSON.stringify({ invoice: INVOICES['1'] }); | |
return [200, { 'Content-Type': 'application/json' }, result]; | |
}); | |
}); | |
}, | |
teardown: function() { | |
Ember.run(App, 'destroy'); | |
server.shutdown(); | |
} | |
}); | |
test('Edit an existing invoice draft', function() { | |
visit('/'); | |
click('table.drafts tr:eq(1) td:eq(3) a'); | |
fillIn('#title', 'Writing Ember.js Learning Material'); | |
click('#update-button'); | |
andThen(function() { | |
equal(find('table.drafts tr:eq(1) td:eq(0)').text(), 'Writing Ember.js Learning Material'); | |
}); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
put '/invoices/:id' do |id| | |
params = JSON.parse request.body.read, symbolize_names: true | |
invoices[id.to_i].merge!(params[:invoice]) | |
{ invoice: invoices[id.to_i] }.to_json | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
export default Ember.Route.extend({ | |
actions: { | |
save: function() { | |
this.modelFor('edit').save(); | |
this.transitionTo('index'); | |
} | |
} | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { formatDate } from '../../../helpers/format-date'; | |
module('format-date Handlebars Helper'); | |
test('handle non-Date objects as input', function() { | |
equal(formatDate(null), ''); | |
equal(formatDate(undefined), ''); | |
equal(formatDate(100), ''); | |
equal(formatDate('a string'), ''); | |
}); | |
test('handle Date object as input', function() { | |
equal(formatDate(new Date(2014, 11, 12)), '12.12.2014'); | |
}); | |
test('prepend single digit days', function() { | |
equal(formatDate(new Date(2014, 11, 2)), '02.12.2014'); | |
}); | |
test('prepend single digit months', function() { | |
equal(formatDate(new Date(2014, 0, 12)), '12.01.2014'); | |
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import Ember from 'ember'; | |
function formatDate(value) { | |
if (value === null || value === undefined) { | |
return ''; | |
} | |
if (!(value instanceof Date)) { | |
return ''; | |
} | |
var day = value.getDate(); | |
var month = value.getMonth() + 1; | |
var year = value.getFullYear(); | |
if (day < 10) { | |
day = '0' + day; | |
} | |
if (month < 10) { | |
month = '0' + month; | |
} | |
return day + '.' + month + '.' + year; | |
} | |
export { formatDate }; | |
export default Ember.Handlebars.makeBoundHelper(formatDate); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
delete '/invoices/:id' do |id| | |
invoices.delete id.to_i | |
200 | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As a small business owner | |
I want to create new invoice drafts | |
So that future me will not forget to bill a client | |
As a small business owner | |
I want to edit invoice drafts | |
So that an invoice draft always mirrors the actual project terms | |
As a small business owner | |
I want to turn an invoice draft into an open invoice | |
So that I remember the invoice is already billed | |
As a small business owner | |
I want to turn an open invoice into a paid invoice | |
So that I remember the invoice is paid | |
As a small business owner | |
I want to delete invoices | |
So that obsolete invoices do not clutter the system | |
As a small business owner | |
I want to see a grouped overview of all invoices in the system | |
So that I can get an insight into my business financials with one look | |
As a small business owner | |
I want to instantly see changes made by my cofounder | |
So that I do not accidently do things which are already taken care of | |
DEFINITIONS | |
Invoice - Represents an actual or future invoice of the business. | |
An invoice consists of a title, client, amount, description, | |
status ('draft', 'open' and 'paid'), billing date, due date and payment date. | |
Invoices can be only edited if they have the 'draft' status. | |
Client - Someone the small business owner is working with. Is only saved as single text field. | |
ROLES | |
Small business owner - Someone running a small business who wants to keep tabs on his invoices. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
current_invoice_id = 4 | |
invoices = { | |
1 => { | |
id: 1, | |
title: 'Developing the frontend', | |
client: 'edgy circle', | |
description: '', | |
amount: 890000, | |
status: 'draft', | |
due_at: nil, | |
billed_at: nil, | |
paid_at: nil | |
}, | |
2 => { | |
id: 2, | |
title: 'Invoice backend development', | |
client: 'edgy circle', | |
description: '', | |
amount: '420000', | |
status: 'open', | |
due_at: (Date.today + 5).to_datetime, | |
billed_at: Date.today.to_datetime, | |
paid_at: nil | |
}, | |
3 => { | |
id: 3, | |
title: 'Requirements Workshop "Invoice Application"', | |
client: 'edgy circle', | |
description: 'Note: Suppies were provided by the client.', | |
amount: 99900, | |
status: 'paid', | |
due_at: (Date.today - 10).to_datetime, | |
billed_at: (Date.today - 13).to_datetime, | |
paid_at: Date.today.to_datetime | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
As a small business owner | |
I want to see a grouped overview of all invoices in the system | |
So that I can get an insight into my business financials with one look |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment