e45557248ba12a15 Services
authorTomas Zeman <tzeman@volny.cz>
Thu, 24 May 2012 11:19:26 +0200
changeset 99 49eb72a46208
parent 98 eac38214183d
child 100 1fcbeae1f9da
e45557248ba12a15 Services
src/main/resources/db/db-data.sql
src/main/resources/db/db-schema.sql
src/main/resources/db/schema-changes-0.2-0.3.sql
src/main/scala/bootstrap/liftweb/Boot.scala
src/main/scala/fis/base/model/Payment.scala
src/main/scala/fis/base/ui/FieldTable.scala
src/main/scala/fis/crm/ui/CompanySnippet.scala
src/main/scala/fis/sr/model/Service.scala
src/main/scala/fis/sr/model/ServiceCrud.scala
src/main/scala/fis/sr/model/ServicePayment.scala
src/main/scala/fis/sr/model/ServicePaymentCrud.scala
src/main/scala/fis/sr/model/ServiceRepoSchema.scala
src/main/scala/fis/sr/ui/ServiceForm.scala
src/main/scala/fis/sr/ui/ServicePaymentTable.scala
src/main/scala/fis/sr/ui/ServiceSnippet.scala
src/main/scala/fis/sr/ui/ServiceTable.scala
src/main/scala/fis/top/model/FisDbSchema.scala
src/main/scala/net/tz/lift/model/JodaTimeField.scala
src/main/webapp/company/view.html
src/main/webapp/service/view.html
src/main/webapp/templates-hidden/_resources.html
src/main/webapp/templates-hidden/_resources_cs.html
src/main/webapp/templates-hidden/nodatatable.html
--- 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);
+
--- 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;
--- 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");
+
--- 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:_*))
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- 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 }) }
--- 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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(<span class="uneditable-input">{asHtml}</span>)
+  }
+
+  @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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- /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 <tzeman@volny.cz>
+ *
+ * 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:
--- 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
 
--- 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:
--- 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 @@
       </div> <!-- /row -->
       <div class="row section">
         <div class="span12">
+          <h3><span class="lift:loc?locid=Services"></span></h3>
+          <span class="lift:services"></span>
+        </div>
+      </div> <!-- /row -->
+      <div class="row section">
+        <div class="span12">
           <h3><span class="lift:loc?locid=Contacts"></span></h3>
           <span class="lift:contacts"></span>
         </div>
--- /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 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
+    <title>Entity View</title>
+  </head>
+  <body class="lift:content_id=main">
+    <div id="main" class="lift:surround?with=default;at=content">
+      <div class="row">
+        <div class="span12">
+          <span class="lift:panel"></span>
+        </div>
+      </div> <!-- /row -->
+      <div class="row section">
+        <span class="lift:payments">
+          <div class="span5">
+            <h3><span class="lift:loc?locid=payment.direction.cost"></span></h3>
+            <span class="cost"></span>
+          </div>
+          <div class="span5">
+            <h3><span class="lift:loc?locid=payment.direction.revenue"></span></h3>
+            <span class="revenue"></span>
+          </div>
+        </span>
+      </div> <!-- /row -->
+    </div>
+  </body>
+</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 @@
   <res name="comment.note" lang="en" default="true">Comment</res>
 
 
+  <!-- service
+    default strings:
+      Service
+      Service %s
+      Edit service %s
+      Delete service %s
+      Really delete this service?
+      Service %s deleted.
+      Service %s saved.
+      Create service
+      Create service for company %s
+      Payment saved.
+      Add cost to service %s
+      Add revenue to service %s
+      Edit %s for service %s
+      Payment deleted.
+      Really delete this payment?
+      Delete %s for service %s
+  -->
+  <res name="Services" lang="en" default="true">Services</res>
+  <!-- service fields -->
+  <res name="service.name" lang="en" default="true">Name</res>
+  <res name="service.note" lang="en" default="true">Note</res>
+  <res name="service.company" lang="en" default="true">Company</res>
+  <res name="service.stateFld" lang="en" default="true">State</res>
+  <res name="service.from" lang="en" default="true">From</res>
+  <res name="service.to" lang="en" default="true">To</res>
+
+  <!-- service states -->
+  <res name="service.state.init" lang="en" default="true">Initialization</res>
+  <res name="service.state.active" lang="en" default="true">Active</res>
+  <res name="service.state.terminated" lang="en" default="true">Terminated</res>
+
+
+  <!-- payments -->
+  <res name="payment.direction.cost" lang="en" default="true">Cost</res>
+  <res name="payment.direction.revenue" lang="en" default="true">Revenue</res>
+  <res name="payment.period.initial" lang="en" default="true">Initial</res>
+  <res name="payment.period.monthly" lang="en" default="true">Monthly</res>
+  <res name="payment.period.quarterly" lang="en" default="true">Quarterly</res>
+  <res name="payment.period.semiannually" lang="en" default="true">Semiannually</res>
+  <res name="payment.period.annually" lang="en" default="true">Annually</res>
+
+  <!-- service payments -->
+  <res name="servicePayment.direction" lang="en" default="true">Payment type</res>
+  <res name="servicePayment.periodFld" lang="en" default="true">Period</res>
+  <res name="servicePayment.amount" lang="en" default="true">Amount</res>
+  <res name="servicePayment.currency" lang="en" default="true">Currency</res>
+
+
+
 <!--
   vim: et sw=2 ts=2
 -->
--- 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 @@
   <res name="comment.note" lang="cs">Komentář</res>
 
 
+  <!-- service -->
+  <res name="Service" lang="cs">Služba</res>
+  <res name="Service %s" lang="cs">Služba %s</res>
+  <res name="Edit service %s" lang="cs">Upravit službu %s</res>
+  <res name="Delete service %s" lang="cs">Smazat službu %s</res>
+  <res name="Really delete this service?" lang="cs">Skutečně smazat službu?</res>
+  <res name="Service %s deleted." lang="cs">Služba %s smazána.</res>
+  <res name="Service %s saved." lang="cs">Služba %s uložena.</res>
+  <res name="Create service" lang="cs">Vytvořit službu</res>
+  <res name="Create service for company %s" lang="cs">Vytvořit službu pro společnost %s</res>
+  <res name="Payment saved." lang="cs">Platba uložena.</res>
+  <res name="Add cost to service %s" lang="cs">Přidat náklady ke službě %s</res>
+  <res name="Add revenue to service %s" lang="cs">Přidat výnosy ke službě %s</res>
+  <res name="Edit %s for service %s" lang="cs">Upravit %s pro službu %s</res>
+  <res name="Payment deleted." lang="cs">Platba smazána.</res>
+  <res name="Really delete this payment?" lang="cs">Skutečně smazat platbu?</res>
+  <res name="Delete %s for service %s" lang="cs">Smazat %s pro službu %s</res>
+  <res name="Services" lang="cs">Služby</res>
+  <!-- service fields -->
+  <res name="service.name" lang="cs">Název</res>
+  <res name="service.note" lang="cs">Poznámka</res>
+  <res name="service.company" lang="cs">Společnost</res>
+  <res name="service.stateFld" lang="cs">Stav</res>
+  <res name="service.from" lang="cs">Od</res>
+  <res name="service.to" lang="cs">Do</res>
+
+  <!-- service states -->
+  <res name="service.state.init" lang="en" default="true">Ve výstavbě</res>
+  <res name="service.state.active" lang="en" default="true">Aktivní</res>
+  <res name="service.state.terminated" lang="en" default="true">Ukončena</res>
+
+  <!-- payments -->
+  <res name="payment.direction.cost" lang="cs">Náklady</res>
+  <res name="payment.direction.revenue" lang="cs">Výnosy</res>
+  <res name="payment.period.initial" lang="cs">Počáteční</res>
+  <res name="payment.period.monthly" lang="cs">Měsíční</res>
+  <res name="payment.period.quarterly" lang="cs">Čtvrtletní</res>
+  <res name="payment.period.semiannually" lang="cs">Pololetní</res>
+  <res name="payment.period.annually" lang="cs">Roční</res>
+
+  <!-- service payments -->
+  <res name="servicePayment.direction" lang="cs">Typ platby</res>
+  <res name="servicePayment.periodFld" lang="cs">Perioda</res>
+  <res name="servicePayment.amount" lang="cs">Částka</res>
+  <res name="servicePayment.currency" lang="cs">Měna</res>
+
+
+
 <!--
   vim: et sw=2 ts=2
 -->
--- /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 @@
+<table class="table table-striped table-bordered table-condensed">
+  <thead>
+    <tr>
+      <th class="field-name">Field Name</th>
+    </tr>
+  </thead>
+  <tbody>
+    <tr class="r">
+      <td>Cell value</td>
+    </tr>
+  </tbody>
+</table>