# HG changeset patch # User Tomas Zeman # Date 1337851166 -7200 # Node ID 49eb72a4620855cedd0d64be06dd189f8e18e059 # Parent eac38214183db3ef6df5334926ec2ac381f9adcd e45557248ba12a15 Services diff -r eac38214183d -r 49eb72a46208 src/main/resources/db/db-data.sql --- a/src/main/resources/db/db-data.sql Wed May 09 22:53:03 2012 +0200 +++ b/src/main/resources/db/db-data.sql Thu May 24 11:19:26 2012 +0200 @@ -137,3 +137,76 @@ VALUES ('company_status', nextval('code_list_item_id_seq'), true, 'company.status.inactive', 30, false, 0, 0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); + +-- Service state +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('service_state', nextval('code_list_item_id_seq'), true, 'service.state.init', 10, true, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('service_state', nextval('code_list_item_id_seq'), true, 'service.state.active', 20, false, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('service_state', nextval('code_list_item_id_seq'), true, 'service.state.terminated', 30, false, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); + +-- Currency +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('currency', nextval('code_list_item_id_seq'), false, 'CZK', 10, true, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('currency', nextval('code_list_item_id_seq'), false, 'EUR', 20, false, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('currency', nextval('code_list_item_id_seq'), false, 'USD', 30, false, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); + +-- Payment period +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('payment_period', nextval('code_list_item_id_seq'), true, 'payment.period.initial', 10, true, 0, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('payment_period', nextval('code_list_item_id_seq'), true, 'payment.period.monthly', 20, false, 12, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('payment_period', nextval('code_list_item_id_seq'), true, 'payment.period.quarterly', 30, false, 4, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('payment_period', nextval('code_list_item_id_seq'), true, 'payment.period.semiannually', 40, false, 2, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); +INSERT INTO code_list_item +(code_list, id, i18n, name, rank, dflt, i1, +i2, i3, l1, l2, l3, s1, s2, s3, created_at, updated_at, deleted) +VALUES +('payment_period', nextval('code_list_item_id_seq'), true, 'payment.period.annually', 50, false, 1, +0, 0, 0, 0, 0, '', '', '', current_timestamp, current_timestamp, false); + diff -r eac38214183d -r 49eb72a46208 src/main/resources/db/db-schema.sql --- a/src/main/resources/db/db-schema.sql Wed May 09 22:53:03 2012 +0200 +++ b/src/main/resources/db/db-schema.sql Thu May 24 11:19:26 2012 +0200 @@ -210,6 +210,29 @@ "location" bigint not null, "project" bigint not null ); +create table "service" ( + "name" varchar(100) not null, + "updated_at" timestamp not null, + "id" bigint primary key not null, + "to" timestamp, + "state" bigint not null, + "company" bigint not null, + "note" varchar(10240), + "from" timestamp not null, + "created_at" timestamp not null, + "created_by" bigint, + "updated_by" bigint + ); +create sequence "service_id_seq"; +create table "service_payment" ( + "id" bigint primary key not null, + "service" bigint not null, + "direction" integer not null, + "period" bigint not null, + "amount" numeric(16,2) not null, + "currency" bigint not null + ); +create sequence "service_payment_id_seq"; -- foreign key constraints : alter table "address" add foreign key ("city_id") references "city"("id"); alter table "city" add foreign key ("country_id") references "country"("id"); @@ -226,7 +249,12 @@ alter table "task" add foreign key ("state") references "code_list_item"("id"); alter table "task" add foreign key ("project_id") references "project"("id"); alter table "comment" add foreign key ("task_id") references "task"("id") on delete cascade; +alter table "service" add foreign key ("company") references "company"("id"); +alter table "service" add foreign key ("state") references "code_list_item"("id"); alter table "company_contact" add foreign key ("entity") references "company"("id") on delete cascade; +alter table "service_payment" add foreign key ("service") references "service"("id") on delete cascade; +alter table "service_payment" add foreign key ("period") references "code_list_item"("id"); +alter table "service_payment" add foreign key ("currency") references "code_list_item"("id"); alter table "company_contact" add foreign key ("contact") references "contact"("id") on delete cascade; alter table "user_contact" add foreign key ("entity") references "user"("id") on delete cascade; alter table "user_contact" add foreign key ("contact") references "contact"("id") on delete cascade; diff -r eac38214183d -r 49eb72a46208 src/main/resources/db/schema-changes-0.2-0.3.sql --- a/src/main/resources/db/schema-changes-0.2-0.3.sql Wed May 09 22:53:03 2012 +0200 +++ b/src/main/resources/db/schema-changes-0.2-0.3.sql Thu May 24 11:19:26 2012 +0200 @@ -8,3 +8,36 @@ alter table "project_location" add foreign key ("project") references "project"("id") on delete cascade; alter table "project_location" add foreign key ("location") references "location"("id") on delete cascade; alter table "project_location" add unique("project","location"); + +-- service +create table "service" ( + "name" varchar(100) not null, + "updated_at" timestamp not null, + "id" bigint primary key not null, + "to" timestamp, + "state" bigint not null, + "company" bigint not null, + "note" varchar(10240), + "from" timestamp not null, + "created_at" timestamp not null, + "created_by" bigint, + "updated_by" bigint + ); +create sequence "service_id_seq"; +alter table "service" add foreign key ("company") references "company"("id"); +alter table "service" add foreign key ("state") references "code_list_item"("id"); + +-- service payment +create table "service_payment" ( + "id" bigint primary key not null, + "service" bigint not null, + "direction" integer not null, + "period" bigint not null, + "amount" numeric(16,2) not null, + "currency" bigint not null + ); +create sequence "service_payment_id_seq"; +alter table "service_payment" add foreign key ("service") references "service"("id") on delete cascade; +alter table "service_payment" add foreign key ("period") references "code_list_item"("id"); +alter table "service_payment" add foreign key ("currency") references "code_list_item"("id"); + diff -r eac38214183d -r 49eb72a46208 src/main/scala/bootstrap/liftweb/Boot.scala --- a/src/main/scala/bootstrap/liftweb/Boot.scala Wed May 09 22:53:03 2012 +0200 +++ b/src/main/scala/bootstrap/liftweb/Boot.scala Thu May 24 11:19:26 2012 +0200 @@ -22,6 +22,7 @@ import fis.crm.ui._ import fis.geo.ui._ import fis.pm.ui._ +import fis.sr.ui._ import fis.db.SquerylTxMgr import net.datatables.DataTables import net.liftweb.common._ @@ -63,7 +64,8 @@ ContactSnippet.menu, CountrySnippet.menu, CitySnippet.menu, - LocationSnippet.menu + LocationSnippet.menu, + ServiceSnippet.menu ) LiftRules.setSiteMap(SiteMap(menus:_*)) diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/base/model/Payment.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/base/model/Payment.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,69 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.base.model + +import fis.cl.model.{CodeList, CodeListItem, CodeListItemField} +import java.math.MathContext +import net.liftweb.record.{MetaRecord, Record} +import net.liftweb.record.field._ +import net.tz.lift.model._ +import net.tz.lift.model.{FieldLabel => FL} +import org.squeryl.annotations.Column + +trait Payment[T <: Record[T]] { self: T => + + val direction = new EnumField(this.asInstanceOf[T], PaymentDirection) with FL + { + override def defaultValue = PaymentDirection.Cost + override def get = PaymentDirection(super.get.id) + } + + @Column("period") + val periodFld = new CodeListItemField(this.asInstanceOf[T], 'payment_period) + with FL + def period: PaymentPeriod = periodFld.item.dmap(PaymentPeriod.empty)(PaymentPeriod(_)) + + val amount = new DecimalField(this.asInstanceOf[T], MathContext.DECIMAL64, 2) + with FL + val currency = new CodeListItemField(this.asInstanceOf[T], 'currency) with FL +} + +object PaymentDirection extends Enumeration { + type PaymentDirection = Value + + val Cost = new L10nVal(1, "payment.direction.cost") + val Revenue = new L10nVal(2, "payment.direction.revenue") + + class L10nVal(id: Int, n: String) extends Val(id, n) { + override def toString = l10n(n) + } +} + +case class PaymentPeriod(perYear: Int, initial: Boolean, i: CodeListItem, + rank: Int) + +object PaymentPeriod { + def apply(i: CodeListItem): PaymentPeriod = + PaymentPeriod(i.i1.get, i.i1.get == 0, i, i.rank.get) + + lazy val empty = apply(CodeListItem.createRecord) + + private lazy val cl = CodeList('payment_period) + + def periods = cl.items map(apply(_)) +} + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/base/ui/FieldTable.scala --- a/src/main/scala/fis/base/ui/FieldTable.scala Wed May 09 22:53:03 2012 +0200 +++ b/src/main/scala/fis/base/ui/FieldTable.scala Thu May 24 11:19:26 2012 +0200 @@ -18,7 +18,7 @@ import net.liftweb.http.Templates import net.liftweb.util._ import net.liftweb.util.Helpers._ // CSS transforms -import scala.xml.NodeSeq +import scala.xml.{NodeSeq, Text} /** * Template driven table, compatible w/ datatables.net. @@ -29,7 +29,8 @@ Templates(List("templates-hidden", "datatable")) openOr NodeSeq.Empty protected def cols(cs: Iterable[ReadableField]) = - ".field-name *" #> (cs map(_.displayName)) + ".field-name *" #> (cs map { f => + f.displayNameHtml openOr Text(f.displayName) }) protected def cells(cs: Iterable[T]) = ".r" #> cs.map { v => "td *" #> (fields(v) map { _.asHtml }) } diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/crm/ui/CompanySnippet.scala --- a/src/main/scala/fis/crm/ui/CompanySnippet.scala Wed May 09 22:53:03 2012 +0200 +++ b/src/main/scala/fis/crm/ui/CompanySnippet.scala Thu May 24 11:19:26 2012 +0200 @@ -23,6 +23,8 @@ import fis.geo.model._ import fis.pm.model.CompanyProjects import fis.pm.ui.ProjectTable +import fis.sr.model.{CompanyServices, Service} +import fis.sr.ui.{ServiceForm, ServiceSnippet, ServiceTable} import net.liftweb.common._ import net.liftweb.http._ import net.liftweb.http.js.JsCmds.RedirectTo @@ -48,7 +50,7 @@ private val viewPre = Menu.param[Company]("company.view", l10n("Company"), parse, encode) / prefix / * >> Title(c => i18n("Company %s", c.linkName)) >> locTpl("company/view") >> Snippet("panel", panel) >> - Snippet("contacts", contacts.contacts) >> + Snippet("services", services) >> Snippet("contacts", contacts.contacts) >> Snippet("projects", projects) >> Hidden private val editPre = Menu.param[Company]("company.edit", l10n("Edit"), parse, @@ -61,18 +63,25 @@ Title(c => i18n("Delete company %s", c.linkName)) >> IfLoggedIn.testVal >> locTpl("entity/delete") >> Snippet("form", deleteF) >> Hidden + private val createServicePre = Menu.param[Company]("company.create-service", + l10n("Create service"), parse, encode) / prefix / * / "create-service" >> + Title(c => i18n("Create service for company %s", c.linkName)) >> + IfLoggedIn.testVal >> + locTpl("entity/form") >> Snippet("form", createServiceForm) >> Hidden + private val listM = listPre >> SecNav(createPre).build private val createM = createPre >> SecNav(listPre).build private val viewM = viewPre >> - (SecNav(editPre) + deletePre + contacts.chooseM).build + (SecNav(editPre) + deletePre + createServicePre + contacts.chooseM).build private val editM = editPre >> SecNav(viewPre).build private val deleteM = deletePre >> SecNav(viewPre).build + private val createServiceM = createServicePre >> SecNav(viewPre).build private lazy val viewLoc = viewM.toLoc private lazy val editLoc = editM.toLoc private lazy val deleteLoc = deleteM.toLoc - val menu = listM submenus(viewM, editM, createM, deleteM, + val menu = listM submenus(viewM, editM, createM, deleteM, createServiceM, contacts.chooseM, contacts.confirmM) private def cur = viewLoc.currentValue or editLoc.currentValue or @@ -85,6 +94,9 @@ private def projects: CssTr = "*" #> cur.map { c => ProjectTable(CompanyProjects(c)) } + private def services: CssTr = "*" #> cur.map { c => + ServiceTable(CompanyServices(c)) } + object url { def view: Company => Box[String] = (viewLoc.calcHref _) andThen (Box !! _) } @@ -248,6 +260,16 @@ } } + private object createServiceForm extends ServiceForm { + override def localSetup() { + createServiceM.currentValue.foreach { c => service.company(c.id) } + } + + protected def onSuccess(s: Service) { + ServiceSnippet.url.view(s).foreach { u => S redirectTo u } + } + } + } // vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/model/Service.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/model/Service.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,54 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.model + +import fis.aaa.model._ +import fis.base.model.Entity +import fis.cl.model._ +import fis.crm.model._ +import net.liftweb.common._ +import net.liftweb.record.{MetaRecord, Record} +import net.liftweb.record.field._ +import net.tz.lift.model._ +import net.tz.lift.model.{FieldLabel => FL} +import org.squeryl.annotations.Column + +class Service private() extends Record[Service] with Entity[Service] { + def meta = Service + + val company = new LongField(this) with CompanyField with FL { + override def toForm = Full({asHtml}) + } + + @Column("state") + protected[sr] val stateFld = new CodeListItemField(this, 'service_state) + with FL + + val from = new JodaDateMidnightField(this) with FL + val to = new OptionalJodaDateMidnightField(this) with FL + + def state = stateFld.item.map(ServiceState(_)) +} + +object Service extends Service with MetaRecord[Service] + +case class ServiceState(id: Long) +object ServiceState { + def apply(i: CodeListItem): ServiceState = ServiceState(i.id) +} + + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/model/ServiceCrud.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/model/ServiceCrud.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,26 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.model + +import fis.base.model.RecordCrud + +trait ServiceCrud extends RecordCrud[Service] { + val table = ServiceRepoSchema.serviceT +} + +object ServiceCrud extends ServiceCrud + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/model/ServicePayment.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/model/ServicePayment.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,44 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.model + +import fis.aaa.model._ +import fis.base.model.Payment +import fis.cl.model._ +import fis.crm.model._ +import java.math.MathContext +import net.liftweb.common._ +import net.liftweb.record.{MetaRecord, Record} +import net.liftweb.record.field._ +import net.tz.lift.model._ +import net.tz.lift.model.{FieldLabel => FL} +import org.squeryl.annotations.Column +import net.liftweb.squerylrecord.KeyedRecord + +class ServicePayment private() extends Record[ServicePayment] with + KeyedRecord[Long] with Payment[ServicePayment] { + + @Column(name="id") + val idField = new LongField(this) + + def meta = ServicePayment + + val service = new LongField(this) +} + +object ServicePayment extends ServicePayment with MetaRecord[ServicePayment] + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/model/ServicePaymentCrud.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/model/ServicePaymentCrud.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,26 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.model + +import fis.base.model.RecordCrud + +trait ServicePaymentCrud extends RecordCrud[ServicePayment] { + val table = ServiceRepoSchema.servicePaymentT +} + +object ServicePaymentCrud extends ServicePaymentCrud + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/model/ServiceRepoSchema.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/model/ServiceRepoSchema.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,60 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.model + +import fis.crm.model.{Company, CrmSchema} +import net.liftweb.squerylrecord.RecordTypeMode._ + +trait ServiceRepoSchema extends CrmSchema { + /* service */ + val serviceT = tableWithSeq[Service] + + val serviceCompany = oneToManyRelation(companyT, serviceT). + via((c, s) => (c.id === s.company)) + + val serviceState = oneToManyRelation(cli, serviceT). + via((i, s) => (i.id === s.stateFld)) + + /* service payment */ + val servicePaymentT = tableWithSeq[ServicePayment] + + val servicePaymentService = oneToManyRelation(serviceT, servicePaymentT). + via((s, p) => (s.id === p.service)) + servicePaymentService.foreignKeyDeclaration.constrainReference(onDelete cascade) + + val servicePaymentPeriod = oneToManyRelation(cli, servicePaymentT). + via((i, p) => (i.id === p.periodFld)) + + val servicePaymentCurrency = oneToManyRelation(cli, servicePaymentT). + via((i, p) => (i.id === p.currency)) + +} + +object ServiceRepoSchema extends ServiceRepoSchema + +object CompanyServices { + def apply(c: Company): Iterable[Service] = + from(ServiceRepoSchema.serviceCompany.left(c))(s => + select(s) orderBy(s.name asc)) +} + +object ServicePayments { + def apply(s: Service): Iterable[ServicePayment] = + from(ServiceRepoSchema.servicePaymentService.left(s))(p => select(p)) +} + + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/ui/ServiceForm.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/ui/ServiceForm.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,47 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.ui + +import fis.base.ui._ +import fis.sr.model._ +import net.liftweb.http._ +import net.liftweb.util._ +import net.tz.lift.model._ + +abstract class ServiceForm extends HorizontalScreen with CancelButton with + SaveButton { + + protected object service extends ScreenVar[Service](Service.createRecord) + + protected def fields(s: Service): List[BaseField] = List(s.name, s.company, + s.stateFld, s.from, s.to, s.note) + + override def screenFields = fields(service) + + protected def onSuccess(s: Service): Unit + + def finish() { + for { + s <- ServiceCrud.save(service) + } { + S notice l10n("Service %s saved.", s.linkName) + onSuccess(s) + } + } + +} + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/ui/ServicePaymentTable.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/ui/ServicePaymentTable.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,35 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.ui + +import fis.sr.model._ +import fis.base.ui._ +import net.liftweb.http.Templates +import net.liftweb.util.ReadableField +import scala.xml.NodeSeq + +trait ServicePaymentTable extends FieldTable[ServicePayment] { + + protected override def load = + Templates(List("templates-hidden", "nodatatable")) openOr NodeSeq.Empty + + protected override def fields(p: ServicePayment): Iterable[ReadableField] = + Seq(p.periodFld, p.amount, p.currency) + + def apply(l: Iterable[ServicePayment]) = build(ServicePayment, l) +} + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/ui/ServiceSnippet.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/ui/ServiceSnippet.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,226 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.ui + +import fis.aaa.ui.IfLoggedIn +import fis.base.model.{PaymentDirection, ReadOnlyField} +import fis.base.ui._ +import fis.crm.ui.CompanySnippet +import fis.sr.model._ +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.sitemap._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.sitemap.Menu._ +import net.liftweb.util._ +import net.liftweb.util.Helpers._ +import net.tz.lift.model._ +import net.tz.lift.snippet._ +import scala.xml.{Elem, NodeSeq, Text} + +object ServiceSnippet extends ServiceCrud with EntitySnippet[Service] { + val prefix = "service" + + private val listM = Menu("service.list", l10n("Services")) / prefix >> + Title(_ => i18n("Services")) >> + locTpl("entity/list") >> Snippet("list", ClearNodes) >> Hidden + + private val viewPre = Menu.param[Service]("service.view", l10n("Service"), + parse, encode) / prefix / * >> Title(t => i18n("Service %s", t.linkName)) >> + locTpl("service/view") >> Snippet("panel", panel) >> + Snippet("payments", payments) >> Hidden + + private val editPre = Menu.param[Service]("service.edit", l10n("Edit"), parse, + encode) / prefix / * / EDIT >> + Title(t => i18n("Edit service %s", t.linkName)) >> IfLoggedIn.testVal >> + locTpl("entity/form") >> Snippet("form", form) >> Hidden + + private val deletePre = Menu.param[Service]("service.delete", l10n("Delete"), + parse, encode) / prefix / * / DELETE >> + Title(t => i18n("Delete service %s", t.linkName)) >> IfLoggedIn.testVal >> + locTpl("entity/delete") >> Snippet("form", deleteF) >> Hidden + + private val addCostM = Menu.param[Service]("service.addCost", l10n("Add"), + parse, encode) / prefix / * / "add-cost" >> + Title(t => i18n("Add cost to service %s", t.linkName)) >> + IfLoggedIn.testVal >> SecNav(viewPre).build >> + locTpl("entity/form") >> Snippet("form", paymentF) >> Hidden + + private val addRevenueM = Menu.param[Service]("service.addRevenue", l10n("Add"), + parse, encode) / prefix / * / "add-revenue" >> + Title(t => i18n("Add revenue to service %s", t.linkName)) >> + IfLoggedIn.testVal >> SecNav(viewPre).build >> + locTpl("entity/form") >> Snippet("form", paymentF) >> Hidden + + type PaS = (ServicePayment, Service) + private object paymentMemo extends RequestMemoize[String, Box[PaS]]() + private def parsePaS(id: String): Box[PaS] = paymentMemo(id, for { + p <- ServicePaymentCrud.get(id) + s <- get(p.service.get) + } yield (p, s)) + + private val editPaymentM = Menu.param[PaS]("service.payment.edit", + l10n("Edit"), parsePaS _, _._1.id.toString) / prefix / "payment" / * / EDIT >> + Title(p => i18n("Edit %s for service %s", p._1.direction, p._2.linkName)) >> + IfLoggedIn.testVal >> + locTpl("entity/form") >> Snippet("form", paymentF) >> Hidden + + private val deletePaymentM = Menu.param[PaS]("service.payment.delete", + l10n("Delete"), parsePaS _, _._1.id.toString) / prefix / "payment" / * / DELETE >> + Title(p => i18n("Delete %s for service %s", p._1.direction, p._2.linkName)) >> + IfLoggedIn.testVal >> + locTpl("entity/form") >> Snippet("form", deletePaymentF) >> Hidden + + private val viewM = viewPre >> (SecNav(editPre) + deletePre).build + private val editM = editPre >> SecNav(viewPre).build + private val deleteM = deletePre >> SecNav(viewPre).build + + private lazy val viewLoc = viewM.toLoc + private lazy val editLoc = editM.toLoc + private lazy val deleteLoc = deleteM.toLoc + + val menu = listM submenus(viewM, editM, deleteM, addCostM, addRevenueM, + editPaymentM, deletePaymentM) + + private def cur = viewLoc.currentValue or editLoc.currentValue or + deleteLoc.currentValue + + private def panel: CssTr = "*" #> cur.map { t => ViewPanel(fields(t)) } + + object url { + def view: Service => Box[String] = (viewLoc.calcHref _) andThen (Box !! _) + } + + private def fields(s: Service) = List(s.name, s.company, s.stateFld, + s.from, s.to, s.note) + + private case class ServiceLink(s: Service) extends EntityLink[Service](s, + url.view) + + EntityLink.register[Service](ServiceLink(_)) + + private object form extends ServiceForm { + + override def localSetup() { + cur.foreach(service(_)) + } + + protected def onSuccess(s: Service) { + S.redirectTo(viewLoc.calcHref(s)) + } + } + + private object deleteF extends HorizontalScreen with CancelButton with + DeleteButton { + + val confirm = field(l10n("Really delete this service?"), false) + + def finish() { + for { + s <- deleteLoc.currentValue if confirm + c <- s.company.vend + u <- CompanySnippet.url.view(c) + r <- delete(s) + n <- r.box(s.linkName) + } { + S notice l10n("Service %s deleted.", n) + S redirectTo u + } + } + } + + def payments: CssTr = "*" #> cur.map { s => + val (cost, revenue) = (ServicePayments(s).toSeq + sortBy(_.period.rank) + partition(_.direction.get == PaymentDirection.Cost)) + ".cost" #> new PaymentTable(s, addCostM).apply(cost) & + ".revenue" #> new PaymentTable(s, addRevenueM).apply(revenue) + } + + private class PaymentTable(s: Service, addM: ParamMenuable[Service]) extends + ServicePaymentTable { + + private def actionFld(p: ServicePayment) = new ReadOnlyField("actions", "", + NodeSeq.Empty, Empty) { + + override def displayNameHtml = + Full(a(addM.toLoc.calcHref(s))(addM.linkText(s))) + + override def asHtml = { + val pas = (p, s) + a(editPaymentM.toLoc.calcHref(pas))(editPaymentM.linkText(pas)) ++ + Text(" ") ++ + a(deletePaymentM.toLoc.calcHref(pas))(deletePaymentM.linkText(pas)) + } + } + + protected override def fields(p: ServicePayment) = super.fields(p) ++ + (addM.toLoc.testAccess == Left(true)).box(actionFld(p)) + } + + private object paymentF extends HorizontalScreen with CancelButton with + SaveButton with ServicePaymentCrud { + + object service extends ScreenVar[Box[Service]](Empty) + object payment extends ScreenVar[Box[ServicePayment]](Empty) + + override def screenFields: List[BaseField] = payment.toList flatMap { p => + List(p.periodFld, p.amount, p.currency) } + + override def localSetup() { + val p = ServicePayment.createRecord + addCostM.toLoc.currentValue.foreach { s => + service(Full(s)) + payment(Full(p.direction(PaymentDirection.Cost).service(s.id))) } + addRevenueM.toLoc.currentValue.foreach { s => + service(Full(s)) + payment(Full(p.direction(PaymentDirection.Revenue).service(s.id))) } + editPaymentM.toLoc.currentValue.foreach { pas => + service(Full(pas._2)) + payment(Full(pas._1)) + } + } + + def finish() { for { + s <- service + u <- url.view(s) + p1 <- payment + p <- save(p1) + } { + S notice l10n("Payment saved.") + S redirectTo u + }} + } + + private object deletePaymentF extends HorizontalScreen with CancelButton with + DeleteButton with ServicePaymentCrud { + + val confirm = field(l10n("Really delete this payment?"), false) + + def finish() { + for { + pas <- deletePaymentM.toLoc.currentValue if confirm + u <- url.view(pas._2) + r <- delete(pas._1) + } { + S notice l10n("Payment deleted.") + S redirectTo u + } + } + } +} + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/sr/ui/ServiceTable.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/fis/sr/ui/ServiceTable.scala Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,27 @@ +/* + * Copyright 2012 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package fis.sr.ui + +import fis.sr.model._ +import fis.base.ui._ + +object ServiceTable extends FieldTable[Service] { + def fields(s: Service) = EntityLink(s) ++ Seq(s.company, s.stateFld, s.from, + s.to, s.note) + def apply(l: Iterable[Service]) = build(Service, l) +} + +// vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/scala/fis/top/model/FisDbSchema.scala --- a/src/main/scala/fis/top/model/FisDbSchema.scala Wed May 09 22:53:03 2012 +0200 +++ b/src/main/scala/fis/top/model/FisDbSchema.scala Thu May 24 11:19:26 2012 +0200 @@ -21,6 +21,7 @@ import fis.crm.model.CrmSchema import fis.geo.model.GeoSchema import fis.pm.model.PmSchema +import fis.sr.model.ServiceRepoSchema trait FisDbSchema extends BaseSchema with CodeListSchema @@ -28,6 +29,7 @@ with CrmSchema with GeoSchema with PmSchema + with ServiceRepoSchema object FisDbSchema extends FisDbSchema diff -r eac38214183d -r 49eb72a46208 src/main/scala/net/tz/lift/model/JodaTimeField.scala --- a/src/main/scala/net/tz/lift/model/JodaTimeField.scala Wed May 09 22:53:03 2012 +0200 +++ b/src/main/scala/net/tz/lift/model/JodaTimeField.scala Thu May 24 11:19:26 2012 +0200 @@ -90,6 +90,29 @@ } /** + * Optional Joda date field. + */ +class OptionalJodaDateMidnightField[T <: Record[T]](rec: T) extends + OptionalDateTimeField(rec) { + override def setFromAny(in: Any): Box[Calendar] = in match { + case dt: AbstractDateTime => setBox(Full(dt.toGregorianCalendar)) + case x => super.setFromAny(x) + } + + def set(dt: Box[AbstractDateTime]): Box[Calendar] = + set(dt map(_.toGregorianCalendar)) + + def date = get map(new DateMidnight(_)) + + override def toString = date map(AsDateMidnight(_)) getOrElse "" + + override def asHtml = Text(toString) + override def toForm = Full(SHtml.text(toString, + s => setBox(AsDateMidnight(s) map {_.toGregorianCalendar}), + "class" -> "date")) +} + +/** * Joda date-time converters. */ object AsDateTime { @@ -106,8 +129,10 @@ val fmt = DateTimeFormat.forPattern("dd.MM.yyyy") def apply(d: DateMidnight): String = fmt.print(d) - def apply(s: String): Box[DateMidnight] = tryo { - fmt.parseDateTime(s).toDateMidnight } + def apply(s: String): Box[DateMidnight] = s match { + case null | "" => Empty + case s => tryo { fmt.parseDateTime(s).toDateMidnight } + } } // vim: set ts=2 sw=2 et: diff -r eac38214183d -r 49eb72a46208 src/main/webapp/company/view.html --- a/src/main/webapp/company/view.html Wed May 09 22:53:03 2012 +0200 +++ b/src/main/webapp/company/view.html Thu May 24 11:19:26 2012 +0200 @@ -13,6 +13,12 @@
+

+ +
+
+
+

diff -r eac38214183d -r 49eb72a46208 src/main/webapp/service/view.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/service/view.html Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,30 @@ + + + + + Entity View + + +
+
+
+ +
+
+
+ +
+

+ +
+
+

+ +
+
+
+
+ + + + diff -r eac38214183d -r 49eb72a46208 src/main/webapp/templates-hidden/_resources.html --- a/src/main/webapp/templates-hidden/_resources.html Wed May 09 22:53:03 2012 +0200 +++ b/src/main/webapp/templates-hidden/_resources.html Thu May 24 11:19:26 2012 +0200 @@ -264,6 +264,57 @@ Comment + + Services + + Name + Note + Company + State + From + To + + + Initialization + Active + Terminated + + + + Cost + Revenue + Initial + Monthly + Quarterly + Semiannually + Annually + + + Payment type + Period + Amount + Currency + + + diff -r eac38214183d -r 49eb72a46208 src/main/webapp/templates-hidden/_resources_cs.html --- a/src/main/webapp/templates-hidden/_resources_cs.html Wed May 09 22:53:03 2012 +0200 +++ b/src/main/webapp/templates-hidden/_resources_cs.html Thu May 24 11:19:26 2012 +0200 @@ -250,6 +250,54 @@ Komentář + + Služba + Služba %s + Upravit službu %s + Smazat službu %s + Skutečně smazat službu? + Služba %s smazána. + Služba %s uložena. + Vytvořit službu + Vytvořit službu pro společnost %s + Platba uložena. + Přidat náklady ke službě %s + Přidat výnosy ke službě %s + Upravit %s pro službu %s + Platba smazána. + Skutečně smazat platbu? + Smazat %s pro službu %s + Služby + + Název + Poznámka + Společnost + Stav + Od + Do + + + Ve výstavbě + Aktivní + Ukončena + + + Náklady + Výnosy + Počáteční + Měsíční + Čtvrtletní + Pololetní + Roční + + + Typ platby + Perioda + Částka + Měna + + + diff -r eac38214183d -r 49eb72a46208 src/main/webapp/templates-hidden/nodatatable.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/templates-hidden/nodatatable.html Thu May 24 11:19:26 2012 +0200 @@ -0,0 +1,12 @@ + + + + + + + + + + + +
Field Name
Cell value