Project - company relation
authorTomas Zeman <tzeman@volny.cz>
Tue, 01 May 2012 21:40:36 +0200
changeset 85 3ef84ec492fa
parent 84 43674362ff5e
child 86 2616b8424462
Project - company relation
db/db-schema.sql
src/main/scala/fis/crm/ui/CompanySnippet.scala
src/main/scala/fis/pm/model/PmSchema.scala
src/main/scala/fis/pm/ui/ProjectSnippet.scala
src/main/scala/fis/pm/ui/ProjectTable.scala
src/main/webapp/company/view.html
src/main/webapp/templates-hidden/_resources.html
src/main/webapp/templates-hidden/_resources_cs.html
--- a/db/db-schema.sql	Fri Apr 27 16:18:28 2012 +0200
+++ b/db/db-schema.sql	Tue May 01 21:40:36 2012 +0200
@@ -201,6 +201,10 @@
     "updated_by" bigint
   );
 create sequence "comment_id_seq";
+create table "project_company" (
+    "project" bigint not null,
+    "company" bigint not null
+  );
 -- foreign key constraints :
 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");
@@ -220,8 +224,11 @@
 alter table "company_contact" add constraint "company_contactFK8" foreign key ("contact") references "contact"("id") on delete cascade;
 alter table "user_contact" add constraint "user_contactFK9" foreign key ("entity") references "user"("id") on delete cascade;
 alter table "user_contact" add constraint "user_contactFK10" foreign key ("contact") references "contact"("id") on delete cascade;
+alter table "project_company" add constraint "project_companyFK19" foreign key ("project") references "project"("id") on delete cascade;
+alter table "project_company" add constraint "project_companyFK20" foreign key ("company") references "company"("id") on delete cascade;
 -- composite key indexes :
 alter table "company_contact" add constraint "company_contactCPK" unique("entity","contact");
 alter table "user_contact" add constraint "user_contactCPK" unique("entity","contact");
+alter table "project_company" add constraint "project_companyCPK" unique("project","company");
 -- column group indexes :
 create index "user_deleted_active_idx" on "user" ("deleted","active");
--- a/src/main/scala/fis/crm/ui/CompanySnippet.scala	Fri Apr 27 16:18:28 2012 +0200
+++ b/src/main/scala/fis/crm/ui/CompanySnippet.scala	Tue May 01 21:40:36 2012 +0200
@@ -19,6 +19,8 @@
 import fis.base.ui._
 import fis.crm.model._
 import fis.geo.model._
+import fis.pm.model.CompanyProjects
+import fis.pm.ui.ProjectTable
 import net.liftweb.common._
 import net.liftweb.http._
 import net.liftweb.http.js.JsCmds.RedirectTo
@@ -44,7 +46,8 @@
   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) >> Hidden
+    Snippet("contacts", contacts.contacts) >>
+    Snippet("projects", projects) >> Hidden
 
   private val editPre = Menu.param[Company]("company.edit", l10n("Edit"), parse,
     encode) / prefix / * / EDIT >>
@@ -77,6 +80,9 @@
 
   private def panel: CssTr = "*" #> cur.map(CompanyPanel(_))
 
+  private def projects: CssTr = "*" #> cur.map { c =>
+    ProjectTable(CompanyProjects(c)) }
+
   object url {
     def view: Company => Box[String] = (viewLoc.calcHref _) andThen (Box !! _)
   }
--- a/src/main/scala/fis/pm/model/PmSchema.scala	Fri Apr 27 16:18:28 2012 +0200
+++ b/src/main/scala/fis/pm/model/PmSchema.scala	Tue May 01 21:40:36 2012 +0200
@@ -63,6 +63,15 @@
   val commentTask = oneToManyRelation(taskT, commentT).
     via((t, c) => (t.id === c.task))
   commentTask.foreignKeyDeclaration.constrainReference(onDelete cascade)
+
+  /* project - company */
+  val projectCompany = manyToManyRelation(projectT, companyT).
+    via[ProjectCompany]((p, c, pc) => (
+      p.id === pc.project,
+      c.id === pc.company
+    ))
+  projectCompany.leftForeignKeyDeclaration.constrainReference(onDelete cascade)
+  projectCompany.rightForeignKeyDeclaration.constrainReference(onDelete cascade)
 }
 
 object PmSchema extends PmSchema
@@ -77,4 +86,20 @@
     from(PmSchema.commentTask.left(t))(c => select(c) orderBy(c.createdAt asc))
 }
 
+object CompanyProjects {
+  import fis.crm.model.Company
+  def apply(c: Company): Iterable[Project] =
+    from(PmSchema.projectCompany.right(c))(p => select(p) orderBy(p.name asc))
+}
+
+/* Many-to-many relations */
+
+import org.squeryl.KeyedEntity
+import org.squeryl.dsl._
+
+case class ProjectCompany(val project: Long, val company: Long)
+  extends KeyedEntity[CompositeKey2[Long, Long]] {
+  def id = CompositeKey2(project, company)
+}
+
 // vim: set ts=2 sw=2 et:
--- a/src/main/scala/fis/pm/ui/ProjectSnippet.scala	Fri Apr 27 16:18:28 2012 +0200
+++ b/src/main/scala/fis/pm/ui/ProjectSnippet.scala	Tue May 01 21:40:36 2012 +0200
@@ -16,9 +16,11 @@
 package fis.pm.ui
 
 import fis.base.ui._
+import fis.crm.model._
 import fis.pm.model._
 import net.liftweb.common._
 import net.liftweb.http._
+import net.liftweb.record.field._
 import net.liftweb.sitemap._
 import net.liftweb.sitemap.Loc._
 import net.liftweb.util._
@@ -85,36 +87,53 @@
     def view: Project => Box[String] = (viewLoc.calcHref _) andThen (Box !! _)
   }
 
-  private def fields(p: Project) = List(p.name, p.identS, p.stateFld, p.createdBy,
-    p.createdAt, p.deadline, p.responsible, p.productLine, p.description, p.note)
-
-  private def formFields(p: Project) = List(p.name, p.identS, p.stateFld,
-    p.deadline, p.responsible, p.productLine, p.description, p.note)
-
-  private object ProjectTable extends FieldTable[Project] {
-    def fields(p: Project) = EntityLink(p) ++ Seq(p.identS, p.deadline,
-      p.responsible, p.description, p.note)
-    def apply(l: Iterable[Project]) = build(Project, l)
-  }
+  private def fields(p: Project) = List(p.name, p.identS, p.stateFld,
+    p.createdBy, p.createdAt, p.deadline, p.responsible, p.productLine,
+    ProjectCompanyField(p), p.description, p.note)
 
   private case class ProjectLink(c: Project) extends EntityLink[Project](c, url.view)
 
   EntityLink.register[Project](ProjectLink(_))
 
-  private object form extends HorizontalScreen with CancelButton with SaveButton {
+  private case class ProjectCompanyField(p: Project) extends OptionalLongField(p)
+    with CompanyField with FieldLabel {
+    override def name = "company"
+    override def defaultValueBox =
+      PmSchema.projectCompany.left(p).headOption.map(_.id)
+  }
+
+  private object form extends HorizontalScreen with CancelButton with
+    SaveButton {
 
     private object project extends ScreenVar[Project](Project.createRecord)
+    private object company extends ScreenVar[CompanyField](
+      ProjectCompanyField(project))
+
+    private def formFields(p: Project) = {
+      List(p.name, p.identS, p.stateFld, p.deadline, p.responsible,
+        p.productLine, company.get, p.description, p.note)
+    }
 
     override def screenFields: List[BaseField] = formFields(project)
 
     override def localSetup() {
-      cur.foreach(project(_))
+      cur.foreach { p => project(p); company(ProjectCompanyField(p)) }
     }
 
-    def finish() { save(project) foreach { v =>
-      S notice l10n("Project %s saved.", v.linkName)
-      S.redirectTo(viewLoc.calcHref(v))
-    }}
+    def finish() {
+      save(project) foreach { p =>
+        val fk = PmSchema.projectCompany.left(p)
+        company.vend match {
+          case Full(c) if fk.exists(_.id == c.id) => // empty, no update
+          case Full(c) =>
+            fk.dissociateAll
+            fk.associate(c)
+          case _ => fk.dissociateAll
+        }
+        S notice l10n("Project %s saved.", p.linkName)
+        S.redirectTo(viewLoc.calcHref(p))
+      }
+    }
   }
 
   private object deleteF extends HorizontalScreen with CancelButton with
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/main/scala/fis/pm/ui/ProjectTable.scala	Tue May 01 21:40:36 2012 +0200
@@ -0,0 +1,29 @@
+/*
+ * 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.pm.ui
+
+import fis.pm.model._
+import fis.base.ui._
+
+object ProjectTable extends FieldTable[Project] {
+  def fields(p: Project) = EntityLink(p) ++ Seq(p.identS, p.deadline,
+    p.responsible, p.description, p.note)
+  def apply(l: Iterable[Project]) = build(Project, l)
+}
+
+
+
+// vim: set ts=2 sw=2 et:
--- a/src/main/webapp/company/view.html	Fri Apr 27 16:18:28 2012 +0200
+++ b/src/main/webapp/company/view.html	Tue May 01 21:40:36 2012 +0200
@@ -17,6 +17,12 @@
           <span class="lift:contacts"></span>
         </div>
       </div> <!-- /row -->
+      <div class="row section">
+        <div class="span12">
+          <h3><span class="lift:loc?locid=Projects"></span></h3>
+          <span class="lift:projects"></span>
+        </div>
+      </div> <!-- /row -->
     </div>
   </body>
 </html>
--- a/src/main/webapp/templates-hidden/_resources.html	Fri Apr 27 16:18:28 2012 +0200
+++ b/src/main/webapp/templates-hidden/_resources.html	Tue May 01 21:40:36 2012 +0200
@@ -190,6 +190,7 @@
       Really delete this project?
       Project %s deleted.
   -->
+  <res name="Projects" lang="en" default="true">Projects</res>
   <!-- project fields -->
   <res name="project.name" lang="en" default="true">Name</res>
   <res name="project.note" lang="en" default="true">Note</res>
@@ -200,6 +201,7 @@
   <res name="project.responsible" lang="en" default="true">Responsible</res>
   <res name="project.stateFld" lang="en" default="true">State</res>
   <res name="project.tasks" lang="en" default="true">Tasks</res>
+  <res name="project.company" lang="en" default="true">Company</res>
   <!-- project states -->
   <res name="project.state.assigned" lang="en" default="true">Assigned</res>
   <res name="project.state.paused" lang="en" default="true">Paused</res>
--- a/src/main/webapp/templates-hidden/_resources_cs.html	Fri Apr 27 16:18:28 2012 +0200
+++ b/src/main/webapp/templates-hidden/_resources_cs.html	Tue May 01 21:40:36 2012 +0200
@@ -190,6 +190,7 @@
   <res name="project.responsible" lang="cs">Odpovědný</res>
   <res name="project.stateFld" lang="cs">Stav</res>
   <res name="project.tasks" lang="cs">Úkoly</res>
+  <res name="project.company" lang="cs">Společnost</res>
   <!-- project states -->
   <res name="project.state.assigned" lang="cs">Přidělen</res>
   <res name="project.state.paused" lang="cs">Pozastaven</res>