Company UI
authorTomas Zeman <tzeman@volny.cz>
Fri, 20 Apr 2012 08:26:24 +0200
changeset 69 b1dc0efd1303
parent 68 f8f8cac057a7
child 70 3a53fa30e03e
Company UI
db/db-schema.sql
src/main/scala/bootstrap/liftweb/Boot.scala
src/main/scala/fis/base/ui/BootstrapScreen.scala
src/main/scala/fis/crm/model/Company.scala
src/main/scala/fis/crm/model/CompanyCrud.scala
src/main/scala/fis/crm/model/CrmSchema.scala
src/main/scala/fis/crm/ui/CompanyPanel.scala
src/main/scala/fis/crm/ui/CompanySnippet.scala
src/main/scala/fis/crm/ui/CompanyTable.scala
src/main/scala/fis/geo/model/Address.scala
src/main/webapp/company/form.html
src/main/webapp/templates-hidden/_resources.html
src/main/webapp/templates-hidden/_resources_cs.html
--- a/db/db-schema.sql	Fri Apr 20 08:26:23 2012 +0200
+++ b/db/db-schema.sql	Fri Apr 20 08:26:24 2012 +0200
@@ -142,7 +142,7 @@
 alter table "address" add constraint "addressFK1" foreign key ("city_id") references "city"("id");
 alter table "city" add constraint "cityFK2" foreign key ("country_id") references "country"("id");
 alter table "company" add constraint "companyFK3" foreign key ("address_id") references "address"("id");
-alter table "company" add constraint "companyFK4" foreign key ("post_adress_id") references "address"("id");
+alter table "company" add constraint "companyFK4" foreign key ("post_adress_id") references "address"("id") on delete set null;
 alter table "bank_account" add constraint "bank_accountFK5" foreign key ("company_id") references "company"("id") on delete cascade;
 -- column group indexes :
 create index "user_deleted_active_idx" on "user" ("deleted","active");
--- a/src/main/scala/bootstrap/liftweb/Boot.scala	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/scala/bootstrap/liftweb/Boot.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -18,7 +18,7 @@
 import fis.base.model._
 import fis.base.ui._
 import fis.aaa.ui.UserSnippet
-import fis.crm.ui.ContactSnippet
+import fis.crm.ui._
 import fis.geo.ui.{CitySnippet, CountrySnippet}
 import fis.db.SquerylTxMgr
 import net.liftweb.common._
@@ -43,6 +43,7 @@
 
     val menus = List(Menu("/", "FIS Main page") / "index" >> Hidden,
       Menu.i("Home") / "" , ContactSnippet.menu,
+      CompanySnippet.menu,
       UserSnippet.menu,
       CountrySnippet.menu,
       CitySnippet.menu)
--- a/src/main/scala/fis/base/ui/BootstrapScreen.scala	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/scala/fis/base/ui/BootstrapScreen.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -25,10 +25,13 @@
 import net.liftweb.http.js.JsCmds._
 import net.liftweb.util._
 import net.liftweb.util.Helpers._
+import net.tz.lift.snippet._
 import scala.xml._
 
 trait BootstrapScreen extends ScreenWizardRendered {
 
+  protected def decorateLine(f: ScreenFieldInfo): CssTr = PassThru
+
   protected override def renderAll(currentScreenNumber: Box[NodeSeq],
                           screenCount: Box[NodeSeq],
                           wizardTop: Box[Elem],
@@ -99,9 +102,8 @@
               }
             }))
 
-            maxNotice map { t =>
-              (".control-group [class+]" #> t.lowerCaseTitle)(line)
-            } getOrElse line
+          val out = (".control-group [class+]" #> maxNotice.map(_.lowerCaseTitle))(line)
+          decorateLine(f)(out)
       }
     }
 
--- a/src/main/scala/fis/crm/model/Company.scala	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/scala/fis/crm/model/Company.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -16,7 +16,7 @@
 package fis.crm.model
 
 import fis.base.model.Entity
-import fis.geo.model.Address
+import fis.geo.model._
 import net.liftweb.common._
 import net.liftweb.record.{MetaRecord, Record}
 import net.liftweb.record.field._
@@ -32,9 +32,9 @@
   val dic = new StringField(this, 40) with FL
   val pin = new IntField(this) with FL
   @Column(name="address_id")
-  val address = new LongField(this) with FL
+  val address = new LongField(this) with AddressField with FL
   @Column(name="post_adress_id")
-  val postAddress = new OptionalLongField(this) with FL
+  val postAddress = new OptionalLongField(this) with AddressField with FL
 }
 
 object Company extends Company with MetaRecord[Company]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/model/CompanyCrud.scala	Fri Apr 20 08:26:24 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.crm.model
+
+import fis.base.model.RecordCrud
+
+trait CompanyCrud extends RecordCrud[Company] {
+  val table = CrmSchema.companyT
+}
+
+object CompanyCrud extends CompanyCrud
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/crm/model/CrmSchema.scala	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/scala/fis/crm/model/CrmSchema.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -47,12 +47,17 @@
 
   val companyPostAddress = oneToManyRelation(addressT, companyT).
     via((a, c) => c.postAddress === a.id)
+  companyPostAddress.foreignKeyDeclaration.constrainReference(onDelete setNull)
 
   val bankAccountT = tableWithSeq[BankAccount]
 
   val companyBankAccounts = oneToManyRelation(companyT, bankAccountT).
     via((c, a) => c.id === a.company)
   companyBankAccounts.foreignKeyDeclaration.constrainReference(onDelete cascade)
+
+  def allCompanies: Iterable[Company] = from(companyT) (c =>
+    select(c) orderBy(c.name asc))
+
 }
 
 object CrmSchema extends CrmSchema
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/ui/CompanyPanel.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -0,0 +1,31 @@
+/*
+ * 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.crm.ui
+
+import fis.crm.model._
+import fis.base.ui.ViewPanel
+import net.liftweb.util.BaseField
+
+object CompanyPanel {
+
+  def fields(c: Company): List[BaseField] =
+    List(c.name, c.ico, c.dic, c.address) ++
+    c.postAddress.get.map{_ => c.postAddress} ++ Seq(c.note)
+
+  def apply(c: Company) = ViewPanel(fields(c))
+}
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/ui/CompanySnippet.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -0,0 +1,172 @@
+/*
+ * 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.crm.ui
+
+import fis.base.ui._
+import fis.crm.model._
+import fis.geo.model._
+import net.liftweb.common._
+import net.liftweb.http._
+import net.liftweb.sitemap._
+import net.liftweb.sitemap.Loc._
+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 CompanySnippet extends CompanyCrud with EntitySnippet[Company] {
+  val prefix = "company"
+
+  private val listPre = Menu("company.list", l10n("Companies")) / prefix >>
+    Title(_ => i18n("Companies")) >>
+    locTpl("entity/list") >> Snippet("list", list)
+
+  private val createPre = Menu("company.create", l10n("Create")) / prefix / ADD >>
+    Title(_ => i18n("Create company")) >>
+    locTpl("company/form") >> Snippet("form", form) >> Hidden
+
+  private val viewPre = Menu.param[Company]("company.view", l10n("Company"), parse,
+    encode) / prefix / * >> Title(c => i18n("Company %s", c.linkName)) >>
+    locTpl("entity/view") >> Snippet("panel", panel) >> Hidden
+
+  private val editPre = Menu.param[Company]("company.edit", l10n("Edit"), parse,
+    encode) / prefix / * / EDIT >>
+    Title(c => i18n("Edit company %s", c.linkName)) >>
+    locTpl("company/form") >> Snippet("form", form) >> Hidden
+
+  private val deletePre = Menu.param[Company]("company.delete", l10n("Delete"),
+    parse, encode) / prefix / * / DELETE >>
+    Title(c => i18n("Delete company %s", c.linkName)) >>
+    locTpl("entity/delete") >> Snippet("form", deleteF) >> Hidden
+
+  private val listM = listPre >> SecNav(createPre).build
+  private val createM = createPre >> SecNav(listPre).build
+  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, createM, deleteM)
+
+  private def cur = viewLoc.currentValue or editLoc.currentValue or
+    deleteLoc.currentValue
+
+  private def list: CssTr = { _ => CompanyTable(CrmSchema.allCompanies) }
+
+  private def panel: CssTr = "*" #> cur.map(CompanyPanel(_))
+
+  object url {
+    def view: Company => Box[String] = (viewLoc.calcHref _) andThen (Box !! _)
+  }
+
+  private case class CompanyLink(c: Company) extends EntityLink[Company](c,
+    url.view)
+
+  EntityLink.register[Company](CompanyLink(_))
+
+  private object form extends HorizontalScreen with CancelButton with SaveButton {
+
+    private object company extends ScreenVar[Company](Company.createRecord)
+    private object address extends ScreenVar[Address](Address.createRecord)
+    private object postAddress extends ScreenVar[Address](Address.createRecord)
+
+    private val hasPostAddr = builder(Company.postAddress.displayName, false,
+      FormFieldId("has_post_address")).
+      help(i18n("Post address differs from company address")).make
+
+    override def screenFields: List[BaseField] =
+      fields(company) flatMap(_.allFields)
+
+    private def labelField(f: BaseField) = new Field {
+      type ValueType = String
+      def default = ""
+      override def name = f.name
+      override def displayName = f.displayName
+      override def toForm = Empty
+      override implicit def manifest = buildIt[String] 
+    }
+
+    private def fields(c: Company): List[FieldContainer] = {
+      List[FieldContainer](c.name, c.ico, c.dic, labelField(c.address)) ++
+      address.formFields ++
+      List[FieldContainer](hasPostAddr) ++
+      postAddress.formFields.map { f => field(f,
+        FormFieldId(f.uniqueFieldId.map { "post_" + _} openOr nextFuncName)) } ++
+      List(c.note)
+    }
+
+    protected override def decorateLine(f: ScreenFieldInfo): CssTr =
+      f.field.uniqueFieldId.filter { _.startsWith("post_") } map { _ =>
+      ".control-group [class+]" #> "post-address" } openOr PassThru
+
+    override def localSetup() { cur.foreach { c =>
+      company(c)
+      c.address.vend.foreach(address(_))
+      c.postAddress.vend.foreach { a =>
+        hasPostAddr.set(true)
+        postAddress(a)
+      }
+    }}
+
+    def finish() {
+      hasPostAddr.get match {
+        case true =>
+          company.postAddress(AddressCrud.save(postAddress).$(logF).map(_.id))
+        case false =>
+          val p = company.postAddress.vend
+          company.postAddress(Empty)
+          p.foreach { AddressCrud.delete _ }
+      }
+      AddressCrud.save(address).$(logF).foreach { a =>
+        save(company.address(a.id)).$(logF).foreach { c =>
+          S notice l10n("Company %s saved.", c.linkName)
+          S.redirectTo(viewLoc.calcHref(c))
+        }
+      }
+    }
+  }
+
+  private def logF[T]: Box[T] => Unit = _ match {
+    case Failure(m, _, _) => S error m
+    case _ =>
+  }
+
+  private object deleteF extends HorizontalScreen with CancelButton with
+    DeleteButton {
+
+    val confirm = field(l10n("Really delete this company?"), false)
+
+    def finish() {
+      for {
+        c <- deleteLoc.currentValue if confirm
+        r <- delete(c)
+        n <- r.box(c.linkName)
+      } {
+        c.address.vend.flatMap(AddressCrud.delete _) $ logF
+        c.postAddress.vend.flatMap(AddressCrud.delete _) $ logF
+        S notice l10n("Company %s deleted.", n)
+        S redirectTo listM.loc.calcDefaultHref
+      }
+    }
+  }
+
+}
+
+// vim: set ts=2 sw=2 et:
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/crm/ui/CompanyTable.scala	Fri Apr 20 08:26:24 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.crm.ui
+
+import fis.crm.model._
+import fis.base.ui._
+
+object CompanyTable extends FieldTable[Company] {
+  def fields(c: Company) = EntityLink(c) ++ Seq(c.ico, c.dic, c.note)
+
+  def apply(l: Iterable[Company]) = build(Company, l) 
+}
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/geo/model/Address.scala	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/scala/fis/geo/model/Address.scala	Fri Apr 20 08:26:24 2012 +0200
@@ -30,6 +30,8 @@
   @Column(name="city_id")
   val city = new LongField(this) with CityField with FL
   val zipCode = new StringField(this, "") with FL
+
+  def formFields = List(streetName, streetNum, zipCode, city)
 }
 
 object Address extends Address with MetaRecord[Address]
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/webapp/company/form.html	Fri Apr 20 08:26:24 2012 +0200
@@ -0,0 +1,29 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=UTF-8" http-equiv="content-type" />
+    <title>Company form</title>
+  </head>
+  <body class="lift:content_id=main">
+    <div id="main" class="lift:surround?with=default;at=content">
+      <head_merge>
+        <script type="text/javascript">
+        $(document).ready(function() {
+          $('.post-address').toggle($('#has_post_address').is(':checked'));
+          $('#has_post_address').bind('change', function() {
+            $('.post-address').toggle($(this).is(':checked'));
+          });
+        });
+        </script>
+      </head_merge>
+
+      <div class="row">
+        <div class="span12">
+          <span class="lift:form"></span>
+        </div>
+      </div> <!-- /row -->
+    </div>
+  </body>
+</html>
+
+
--- a/src/main/webapp/templates-hidden/_resources.html	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/webapp/templates-hidden/_resources.html	Fri Apr 20 08:26:24 2012 +0200
@@ -98,6 +98,34 @@
   <res name="city.country" lang="en" default="true">Country</res>
 
 
+  <!-- company
+    default strings:
+      Companies
+      Create company
+      Company %s
+      Edit company %s
+      Delete company %s
+      Post address differs from company address
+      Company %s saved.
+      Really delete this company?
+      Company %s deleted.
+   -->
+  <!-- company fields -->
+  <res name="company.name" lang="en" default="true">Name</res>
+  <res name="company.note" lang="en" default="true">Note</res>
+  <res name="company.ico" lang="en" default="true">ICO</res>
+  <res name="company.dic" lang="en" default="true">DIC</res>
+  <res name="company.pin" lang="en" default="true">Pin</res>
+  <res name="company.address" lang="en" default="true">Address</res>
+  <res name="company.postAddress" lang="en" default="true">Post address</res>
+
+
+  <!-- address -->
+  <res name="address.streetName" lang="en" default="true">Street</res>
+  <res name="address.streetNum" lang="en" default="true">No.</res>
+  <res name="address.city" lang="en" default="true">City</res>
+  <res name="address.zipCode" lang="en" default="true">ZIP</res>
+
 <!--
   vim: et sw=2 ts=2
 -->
--- a/src/main/webapp/templates-hidden/_resources_cs.html	Fri Apr 20 08:26:23 2012 +0200
+++ b/src/main/webapp/templates-hidden/_resources_cs.html	Fri Apr 20 08:26:24 2012 +0200
@@ -95,6 +95,35 @@
   <res name="city.country" lang="en" default="true">Stát</res>
 
 
+  <!-- company -->
+  <res name="Companies" lang="cs">Společnosti</res>
+  <res name="Create company" lang="cs">Vytvořit společnost</res>
+  <res name="Company %s" lang="cs">Společnost %s</res>
+  <res name="Edit company %s" lang="cs">Upravit společnost</res>
+  <res name="Delete company %s" lang="cs">Smazat společnost</res>
+  <res name="Post address differs from company address" lang="cs">Korespondenční adresa se liší od sídla společnosti</res>
+  <res name="Company %s saved." lang="cs">Společnost %s uložena.</res>
+  <res name="Really delete this company?" lang="cs">Skutečně smazat společnost?</res>
+  <res name="Company %s deleted." lang="cs">Společnost %s smazána.</res>
+
+  <!-- company fields -->
+  <res name="company.name" lang="cs">Název</res>
+  <res name="company.note" lang="cs">Poznámka</res>
+  <res name="company.ico" lang="cs">IČO</res>
+  <res name="company.dic" lang="cs">DIČ</res>
+  <res name="company.pin" lang="cs">Pin</res>
+  <res name="company.address" lang="cs">Sídlo</res>
+  <res name="company.postAddress" lang="cs">Korespondenční adresa</res>
+
+
+  <!-- address -->
+  <res name="address.streetName" lang="cs">Ulice</res>
+  <res name="address.streetNum" lang="cs">Číslo (č.o./č.p.)</res>
+  <res name="address.city" lang="cs">Obec</res>
+  <res name="address.zipCode" lang="cs">PSČ</res>
+
+
+
 <!--
   vim: et sw=2 ts=2
 -->