Import invoices
authorelgekko <vasary@elgekko.net>
Wed, 13 Sep 2023 20:44:49 +0000 (22:44 +0200)
committerelgekko <vasary@elgekko.net>
Wed, 13 Sep 2023 20:44:49 +0000 (22:44 +0200)
56 files changed:
lis-app/pom.xml
lis-app/src/main/java/hu/user/lis/LoggerConfiguration.java [deleted file]
lis-app/src/main/java/hu/user/lis/WebAppConfigurer.java
lis-app/src/main/resources/application-dev.yaml
lis-app/src/test/java/hu/user/lis/workflow/ImportIncomingInvoicesIT.java
lis-ui/pom.xml
lis-ui/src/main/java/hu/user/lis/ui/Constants.java
lis-ui/src/main/java/hu/user/lis/ui/auth/Guard.java
lis-ui/src/main/java/hu/user/lis/ui/config/ResourceConfigurer.java
lis-ui/src/main/java/hu/user/lis/ui/config/WebSecurityConfig.java
lis-ui/src/main/java/hu/user/lis/ui/data/ApproveInvoicesDataModel.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/data/AssociateSelectorDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/AssociatesDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/ImportInvoicesDataModel.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/data/PartnerSelectorDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/PartnersDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/ProjectSelectorDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/ProjectStatusDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/ProjectsDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/ServiceRecordsDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/SuppliersDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/SuppliersSimpleDataModel.java
lis-ui/src/main/java/hu/user/lis/ui/data/common/CachedDataModel.java [moved from lis-ui/src/main/java/hu/user/lis/ui/data/CachedDataModel.java with 99% similarity]
lis-ui/src/main/java/hu/user/lis/ui/data/common/CachedSpringDataModel.java [moved from lis-ui/src/main/java/hu/user/lis/ui/data/CachedSpringDataModel.java with 98% similarity]
lis-ui/src/main/java/hu/user/lis/ui/data/common/FormDocument.java [moved from lis-ui/src/main/java/hu/user/lis/ui/data/FormDocument.java with 95% similarity]
lis-ui/src/main/java/hu/user/lis/ui/editor/ImportInvoiceApproveEditorModel.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/editor/ImportInvoiceAssignEditorModel.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/editor/InvoiceEditorModel.java
lis-ui/src/main/java/hu/user/lis/ui/editor/common/Editors.java
lis-ui/src/main/java/hu/user/lis/ui/editor/selector/AssociateSelectorModel.java
lis-ui/src/main/java/hu/user/lis/ui/editor/selector/EntitySelectorModel.java
lis-ui/src/main/java/hu/user/lis/ui/editor/selector/PartnerSelectorModel.java
lis-ui/src/main/java/hu/user/lis/ui/editor/selector/ProjectSelectorModel.java
lis-ui/src/main/java/hu/user/lis/ui/event/EventBus.java
lis-ui/src/main/java/hu/user/lis/ui/event/ProcessEventRouter.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/view/ApproveInvoicesViewModel.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/view/AssignInvoicesViewModel.java [new file with mode: 0644]
lis-ui/src/main/java/hu/user/lis/ui/view/AssociatesViewModel.java
lis-ui/src/main/java/hu/user/lis/ui/view/IndexViewModel.java
lis-ui/src/main/java/hu/user/lis/ui/view/PartnersViewModel.java
lis-ui/src/main/java/hu/user/lis/ui/view/ProjectsViewModel.java
lis-ui/src/main/java/hu/user/lis/ui/view/ServiceRecordsViewModel.java
lis-ui/src/main/java/hu/user/lis/ui/view/common/EntityViewModel.java
lis-ui/src/main/resources/web/editor/import-invoice-approve-editor.zul [new file with mode: 0644]
lis-ui/src/main/resources/web/editor/import-invoice-assign-editor.zul [new file with mode: 0644]
lis-ui/src/main/resources/web/import-invoices-approve.zul [new file with mode: 0644]
lis-ui/src/main/resources/web/import-invoices-assign.zul [new file with mode: 0644]
lis-ui/src/main/resources/web/index.zul
lis-workflow/pom.xml
lis-workflow/src/main/java/hu/user/lis/workflow/event/ImportInvoicesEvent.java [new file with mode: 0644]
lis-workflow/src/main/java/hu/user/lis/workflow/event/ProcessEvent.java [new file with mode: 0644]
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/DownloadInvoiceData.java
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/ListNewInvoices.java
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/IncomingInvoiceFetcherService.java [new file with mode: 0644]
lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/WorkflowManagerService.java [new file with mode: 0644]
lis-workflow/src/main/resources/import-incoming-invoices.bpmn

index 815ef70bbb31b5e0a2a805975970db8ea0bdc369..555887232d39fe14a37afda9596e2924b32d55bd 100644 (file)
@@ -3,7 +3,7 @@
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <artifactId>lis-app</artifactId>
-    <version>0.1.4-SNAPSHOT</version>
+    <version>0.1.5-SNAPSHOT</version>
     <parent>
         <groupId>hu.user</groupId>
         <artifactId>lis</artifactId>
diff --git a/lis-app/src/main/java/hu/user/lis/LoggerConfiguration.java b/lis-app/src/main/java/hu/user/lis/LoggerConfiguration.java
deleted file mode 100644 (file)
index 17f3fd6..0000000
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Copyright (c) $today.year-$today.month-24.
- * By elGekko
- */
-
-package hu.user.lis;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.InjectionPoint;
-import org.springframework.beans.factory.config.ConfigurableBeanFactory;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Scope;
-
-@Configuration
-public class LoggerConfiguration {
-
-    @Bean
-    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
-    public Logger logger(InjectionPoint ip) {
-        final Class lClass;
-        if (ip.getMethodParameter() == null)
-            lClass = ip.getField().getDeclaringClass();
-        else
-            lClass = ip.getMethodParameter().getContainingClass();
-        return LoggerFactory.getLogger(lClass);
-    }
-
-}
\ No newline at end of file
index 0378be2a35db995f89174ef2bed0aa7489c2a398..2527828a684c102fcc89c8ceb5e4dadb4062ca48 100644 (file)
@@ -5,27 +5,24 @@
 
 package hu.user.lis;
 
-import org.slf4j.Logger;
-import org.springframework.beans.factory.annotation.Autowired;
+import lombok.extern.log4j.Log4j2;
 import org.springframework.context.annotation.Configuration;
 import org.springframework.web.servlet.config.annotation.EnableWebMvc;
 import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
 import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
 
 
+@Log4j2
 @Configuration
 @EnableWebMvc
 public class WebAppConfigurer implements WebMvcConfigurer {
-    @Autowired
-    private Logger logger;
-
     //Fajlok kiszolgalasa kulso mappabol es a war-bol
     @Override
     public void addResourceHandlers(ResourceHandlerRegistry registry) {
         registry
                 .addResourceHandler("/files/**")
                 .addResourceLocations("file:/opt/");
-        logger.info("Resource handlers added");
+        log.info("Resource handlers added");
 //        registry
 //                .addResourceHandler("/resources/**")
 //                .addResourceLocations("/resources/");
index dc317389f540df5d8b367d3dd6a37acf0e283125..adea4f1922a4187b40a437a58b89866385887b3a 100644 (file)
@@ -9,7 +9,7 @@ spring:
   jpa:
     #    hibernate:
     #      use-new-id-generator-mappings: false
-    show-sql: true
+    show-sql: false
     properties:
       hibernate:
         format_sql: true
index dc440d5f697d5ef0871a1af15bd7313ee9be49f1..c365f1e1784881cc76dcafd8926977c1533946d5 100644 (file)
@@ -7,7 +7,9 @@ package hu.user.lis.workflow;
 
 import lombok.extern.log4j.Log4j2;
 import org.camunda.bpm.engine.RuntimeService;
+import org.camunda.bpm.engine.TaskService;
 import org.camunda.bpm.engine.runtime.ProcessInstance;
+import org.camunda.bpm.engine.task.Task;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -17,6 +19,8 @@ import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.TestPropertySource;
 import org.springframework.test.context.junit4.SpringRunner;
 
+import java.util.List;
+
 
 @Log4j2
 @SpringBootTest
@@ -25,15 +29,60 @@ import org.springframework.test.context.junit4.SpringRunner;
 @ComponentScan("hu.user.lis")
 @TestPropertySource("classpath:application-dev.yaml")
 public class ImportIncomingInvoicesIT {
+    //    @Rule
+//    public ProcessEngineRule processEngineRule = new ProcessEngineRule();
     @Autowired
     private RuntimeService runtimeService;
 
+    @Autowired
+    private TaskService taskService;
+
+//    @Test
+//    @Deployment(resources = {"import-incoming-invoices.bpmn"})
+//    public void testImportIncomingInvoiceAspect() throws InterruptedException {
+//        RuntimeService runtimeService = processEngineRule.getRuntimeService();
+//        runtimeService.startProcessInstanceByKey("importIncomingInvoices");
+//
+//        TaskService taskService = processEngineRule.getTaskService();
+//        List<Task> taskList = taskService.createTaskQuery().list();
+//        taskList.forEach(t -> {
+//            assertThat(t.getTaskDefinitionKey()).isEqualTo("assignProjectAndAttachFile");
+//            taskService.complete(t.getId());
+//        });
+//    }
+
     @Test
     public void testImportIncomingInvoice() throws InterruptedException {
+        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().list();
+        processInstances.forEach(p -> {
+            //log.info("{} {}", p.getProcessInstanceId(), p.getProcessDefinitionId());
+            try {
+                runtimeService.deleteProcessInstance(p.getProcessInstanceId(), "Canceled");
+            } catch (Exception e) {
+            }
+            //taskService.complete(t.getId());
+        });
+
+        processInstances = runtimeService.createProcessInstanceQuery().list();
+        processInstances.forEach(p -> {
+            log.info("{} {}", p.getProcessInstanceId(), p.getProcessDefinitionId());
+            //taskService.complete(t.getId());
+        });
 
         ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("importIncomingInvoices");
+        processInstances = runtimeService.createProcessInstanceQuery().list();
+        processInstances.forEach(p -> {
+            log.info("{} {}", p.getProcessInstanceId(), p.getProcessDefinitionId());
+            //taskService.complete(t.getId());
+        });
+
+
+        List<Task> taskList = taskService.createTaskQuery().processInstanceId(processInstance.getId()).list();
+        taskList.forEach(t -> {
+            log.info(t.getTaskDefinitionKey());
+            //taskService.complete(t.getId());
+        });
 
-        //verify(processInstance).hasFinished("WorkFinished");
         while (true) {
             ProcessInstance active = runtimeService.createProcessInstanceQuery().processInstanceId(processInstance.getProcessInstanceId()).active().singleResult();
             if (active == null) {
index eaa4fb0432f7c6d8ddd0eacff095ee5ffb08b6a1..fec1ded9568b41e88f420a13610a02d4d063dd30 100644 (file)
             <artifactId>lis-services</artifactId>
             <version>0.0.1-SNAPSHOT</version>
         </dependency>
+        <dependency>
+            <groupId>hu.user</groupId>
+            <artifactId>lis-workflow</artifactId>
+            <version>0.0.1-SNAPSHOT</version>
+        </dependency>
     </dependencies>
 </project>
\ No newline at end of file
index 764deb28dd3817c6c8ae8fb40ff6bcd2be6100d5..336eaa031ef309acd9b9996f36e76e9286b02686 100644 (file)
@@ -3,22 +3,29 @@ package hu.user.lis.ui;
 public class Constants {
     public static final String BINDING_QUEUE = "$ZKBIND_DEFQUE$";
     public static final String PROJECT_EDITOR_QUEUE = "PROJECT_EDITOR_QUEUE";
+    public static final String PROCESS_EVENT_QUEUE = "PROJECT_EDITOR_QUEUE";
     public static final String SHOW_PROJECT_EDITOR = "SHOW_PROJECT_EDITOR";
     public static final String SHOW_PROJECTS_LIST = "SHOW_PROJECTS_LIST";
     public static final String SHOW_SERVICE_RECORDS_LIST = "SHOW_SERVICE_RECORDS_LIST";
     public static final String SET_SERVICE_RECORDS_LIST_DATA = "SET_SERVICE_RECORDS_LIST_DATA";
     public static final String SET_PROJECT_EDITOR_DATA = "SET_PROJECT_EDITOR_DATA";
     public static final String SET_PROJECTS_LIST_DATA = "SET_PROJECTS_LIST_DATA";
-
+    public static final String REFRESH_INVOICE_IMPORT_TASKS = "REFRESH_INVOICE_IMPORT_TASKS";
     public static final String NAV_PARTNERS = "/partners";
     public static final String NAV_PROJECTS = "/projects";
     public static final String NAV_PROJECT = "/project";
+    public static final String NAV_PROJECT_WILD_CARD = "/project/**";
     public static final String NAV_ASSOCIATES = "/associates";
     public static final String NAV_PROJECT_ASSOCIATES = "/project-associates";
     public static final String NAV_SERVICE_RECORDS = "/service-records";
     public static final String NAV_SETTINGS = "/settings";
+    public static final String NAV_IMPORT_INVOICES_ASSIGN = "/import-invoices-assign";
+    public static final String NAV_IMPORT_INVOICES_APPROVE = "/import-invoices-approve";
+    public static final String NAV_LOGIN = "/login";
+    public static final String NAV_LOGOUT = "/logout";
+    public static final String NAV_ROOT = "/";
 
-    public static final String WIDGET_ENTITY_SELECTOR = "~./widget/entity-selector.zul";
-
+    public static final String NAV_API = "/api";
 
+    public static final String WIDGET_ENTITY_SELECTOR = "~./widget/entity-selector.zul";
 }
index 4dbac6780ed92adcb1f7e5c02a3812f38419d1f1..9927115c1f54b7612cad8a2e3b7c7372cf7cb610 100644 (file)
@@ -1,5 +1,6 @@
 package hu.user.lis.ui.auth;
 
+import hu.user.lis.ui.Constants;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.security.authentication.AnonymousAuthenticationToken;
 import org.springframework.security.core.Authentication;
@@ -18,7 +19,7 @@ public class Guard implements Initiator {
         Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 
         if (authentication == null || authentication instanceof AnonymousAuthenticationToken) {
-            Executions.sendRedirect("/login");
+            Executions.sendRedirect(Constants.NAV_LOGIN);
         }
     }
 
index 51e39fea064d5a8aaaaf277a50aaa04396f7485b..3cca4dd98a96f479f0faaa7642a65e8c83f1621e 100644 (file)
@@ -1,5 +1,6 @@
 package hu.user.lis.ui.config;
 
+import hu.user.lis.ui.Constants;
 import org.springframework.stereotype.Controller;
 import org.springframework.web.bind.annotation.GetMapping;
 
@@ -15,7 +16,17 @@ public class ResourceConfigurer {
         return "login";
     }
 
-    @GetMapping({"/partners", "/projects", "/associates", "/project-associates", "/service-records", "/settings", "/project/**"})
+    @GetMapping({
+            Constants.NAV_PARTNERS,
+            Constants.NAV_PROJECTS,
+            Constants.NAV_ASSOCIATES,
+            Constants.NAV_PROJECT_ASSOCIATES,
+            Constants.NAV_SERVICE_RECORDS,
+            Constants.NAV_IMPORT_INVOICES_ASSIGN,
+            Constants.NAV_IMPORT_INVOICES_APPROVE,
+            Constants.NAV_SETTINGS,
+            Constants.NAV_PROJECT_WILD_CARD
+    })
     public String index() {
         return "index";
     }
index ac9de864b13b2dacf73b1906d8d462cd462569c5..27916b9ded939896fc16530a2ab6024f54f89e10 100644 (file)
@@ -1,5 +1,6 @@
 package hu.user.lis.ui.config;
 
+import hu.user.lis.ui.Constants;
 import hu.user.lis.ui.auth.LdapUserDetailsContextMapper;
 import hu.user.lis.ui.auth.LocalAuthProvider;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -12,8 +13,6 @@ import org.springframework.security.config.annotation.web.configuration.WebSecur
 import org.springframework.security.web.authentication.Http403ForbiddenEntryPoint;
 import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
 
-import static hu.user.lis.ui.view.IndexViewModel.NAVIGATION;
-
 @Configuration
 @EnableWebSecurity
 public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@@ -33,7 +32,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
     @Override
     protected void configure(HttpSecurity http) throws Exception {
         http.csrf().disable();
-        String[] navigation = NAVIGATION.keySet().toArray(new String[]{});
+        //String[] navigation = NAVIGATION.keySet().toArray(new String[]{});
         http.headers().frameOptions().sameOrigin();
         http.authorizeRequests()
                 .antMatchers(ZUL_FILES).denyAll() // block direct access to zul files
@@ -41,14 +40,14 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
                 .antMatchers(HttpMethod.POST, "/zkau/upload/**").permitAll() // allow zk resources
                 .regexMatchers(HttpMethod.GET, REMOVE_DESKTOP_REGEX).permitAll() // allow desktop cleanup
                 .requestMatchers(req -> "rmDesktop".equals(req.getParameter("cmd_0"))).permitAll() // allow desktop cleanup from ZATS
-                .mvcMatchers("/", "/login", "/logout", "/api/**").permitAll()
+                .mvcMatchers(Constants.NAV_ROOT, Constants.NAV_LOGIN, Constants.NAV_LOGOUT, Constants.NAV_API + "/**").permitAll()
                 //mvcMatchers(navigation).hasRole("USER")
                 .anyRequest().authenticated()
                 .and()
                 .formLogin()
-                .loginPage("/login").defaultSuccessUrl("/partners")
+                .loginPage(Constants.NAV_LOGIN).defaultSuccessUrl(Constants.NAV_PARTNERS)
                 .and()
-                .logout().logoutUrl("/logout").logoutSuccessUrl("/")
+                .logout().logoutUrl(Constants.NAV_LOGOUT).logoutSuccessUrl(Constants.NAV_LOGIN)
                 .and()
                 .exceptionHandling()
                 .defaultAuthenticationEntryPointFor(
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/data/ApproveInvoicesDataModel.java b/lis-ui/src/main/java/hu/user/lis/ui/data/ApproveInvoicesDataModel.java
new file mode 100644 (file)
index 0000000..8176a60
--- /dev/null
@@ -0,0 +1,58 @@
+package hu.user.lis.ui.data;
+
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
+import hu.user.lis.workflow.event.ImportInvoicesEvent;
+import hu.user.lis.workflow.invoice.service.WorkflowManagerService;
+import lombok.extern.log4j.Log4j2;
+import org.camunda.bpm.engine.task.Task;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.ApplicationListener;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.zkoss.json.JSONObject;
+import org.zkoss.zul.FieldComparator;
+import org.zkoss.zul.event.ListDataEvent;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Component
+@Log4j2
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class ApproveInvoicesDataModel extends CachedSpringDataModel<JSONObject> implements ApplicationListener<ImportInvoicesEvent> {
+    @Autowired
+    WorkflowManagerService workflowManagerService;
+
+    public void refresh() {
+        super.reset();
+    }
+
+    @Override
+    public void onApplicationEvent(ImportInvoicesEvent event) {
+        fireEvent(ListDataEvent.STRUCTURE_CHANGED, -1, -1);
+    }
+
+    public void completeTask(JSONObject selectedEntity, Map<String, Object> vars) {
+        String taskId = (String) selectedEntity.get("taskId");
+        workflowManagerService.completeTask(taskId, vars);
+    }
+
+    @Override
+    public List<JSONObject> getResultSet(int page, int pageSize, FieldComparator sortComparator) {
+        List<Task> tasks = workflowManagerService.listApproveInvoiceTasks();
+        return tasks.stream().map(t -> {
+            JSONObject o = new JSONObject();
+            o.put("taskId", t.getId());
+            o.putAll(workflowManagerService.getVariables(t.getId()));
+            log.info("Task {}", o);
+            return o;
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public int getResultSetCount() {
+        return workflowManagerService.listApproveInvoiceTasks().size();
+    }
+}
index 608c43497f3aae3ee607c3689d2d6d7ee78b8f93..1c0740441ab1bfce77fe2c93dd7409adcce2f253 100644 (file)
@@ -2,6 +2,7 @@ package hu.user.lis.ui.data;
 
 import hu.user.lis.db.Associate;
 import hu.user.lis.db.repository.AssociateRepository;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
index 2f2da1c1c9d0cbf8544b68a2e6003a935c868c4a..2064cf84a27938cd3dcd228089027c9a68452a13 100644 (file)
@@ -4,6 +4,7 @@ import hu.user.lis.db.Associate;
 import hu.user.lis.db.repository.AssociateRepository;
 import hu.user.lis.services.data.AssociateService;
 import hu.user.lis.services.data.EntityDataService;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/data/ImportInvoicesDataModel.java b/lis-ui/src/main/java/hu/user/lis/ui/data/ImportInvoicesDataModel.java
new file mode 100644 (file)
index 0000000..6ad8f8e
--- /dev/null
@@ -0,0 +1,50 @@
+package hu.user.lis.ui.data;
+
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
+import hu.user.lis.workflow.invoice.service.WorkflowManagerService;
+import lombok.extern.log4j.Log4j2;
+import org.camunda.bpm.engine.task.Task;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.config.ConfigurableBeanFactory;
+import org.springframework.context.annotation.Scope;
+import org.springframework.stereotype.Component;
+import org.zkoss.json.JSONObject;
+import org.zkoss.zul.FieldComparator;
+
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+@Component
+@Log4j2
+@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
+public class ImportInvoicesDataModel extends CachedSpringDataModel<JSONObject> {
+    @Autowired
+    WorkflowManagerService workflowManagerService;
+
+    public void refresh() {
+        super.reset();
+    }
+
+    public void completeTask(JSONObject selectedEntity, Map<String, Object> vars) {
+        String taskId = (String) selectedEntity.get("taskId");
+        workflowManagerService.completeTask(taskId, vars);
+    }
+
+    @Override
+    public List<JSONObject> getResultSet(int page, int pageSize, FieldComparator sortComparator) {
+        List<Task> tasks = workflowManagerService.listAssignInvoiceTasks();
+        return tasks.stream().map(t -> {
+            JSONObject o = new JSONObject();
+            o.put("taskId", t.getId());
+            o.putAll(workflowManagerService.getVariables(t.getId()));
+            log.info("Task {}", o);
+            return o;
+        }).collect(Collectors.toList());
+    }
+
+    @Override
+    public int getResultSetCount() {
+        return workflowManagerService.listAssignInvoiceTasks().size();
+    }
+}
index 8149566d482968334c379428e54b1664d8bb3cd6..766f7dcb4cdc7cb2e68dfbcd3151a83a37cb9c93 100644 (file)
@@ -2,6 +2,7 @@ package hu.user.lis.ui.data;
 
 import hu.user.lis.db.Partner;
 import hu.user.lis.db.repository.PartnerRepository;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
index 155d20b9dc10bcf283d4b2f972969c074b045ee5..2d3f704303cab30f6e8f1ebfcf273adf5a305e24 100644 (file)
@@ -4,6 +4,7 @@ import hu.user.lis.db.Partner;
 import hu.user.lis.db.repository.PartnerRepository;
 import hu.user.lis.services.data.EntityDataService;
 import hu.user.lis.services.data.PartnerService;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
index de01efa5a16491f148e30e5c10651e17bc718a8d..a969e1ce6d9766a3694d7838dda88852d6f0fd9e 100644 (file)
@@ -2,6 +2,7 @@ package hu.user.lis.ui.data;
 
 import hu.user.lis.db.Project;
 import hu.user.lis.db.repository.ProjectRepository;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
index cd38b81ec8bf0951d3e0e9cd5b03d7cb772f2285..5297d02e40987228ab4c37c5f7dce0da69df4347 100644 (file)
@@ -3,6 +3,7 @@ package hu.user.lis.ui.data;
 import hu.user.lis.db.ProjectStatus;
 import hu.user.lis.db.repository.ProjectStatusRepository;
 import hu.user.lis.services.data.ProjectStatusService;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
index c3b050bf111573d09c0abb65cf812981fcaf494a..f9972ff27276599ab5227c67ff2748f0d6c1f79a 100644 (file)
@@ -4,6 +4,7 @@ import hu.user.lis.db.Project;
 import hu.user.lis.db.ProjectStatus;
 import hu.user.lis.db.repository.ProjectRepository;
 import hu.user.lis.services.data.EntityDataService;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
index d03345668dd491713ed09b00fb8aacab702dd9f3..4ad69149d79391fa4dfef54b3d4691ada30fb910 100644 (file)
@@ -6,6 +6,7 @@ import hu.user.lis.db.ServiceRecord;
 import hu.user.lis.db.repository.ServiceRecordRepository;
 import hu.user.lis.services.data.EntityDataService;
 import hu.user.lis.services.data.ServiceRecordService;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.config.ConfigurableBeanFactory;
index f7e523598d9a2e575a2d92f1e8ce54eee02c0025..43ab7a6d1319278b4a6c1c52a7a94707f6506b9c 100644 (file)
@@ -2,6 +2,7 @@ package hu.user.lis.ui.data;
 
 import hu.user.lis.db.Supplier;
 import hu.user.lis.services.data.SupplierService;
+import hu.user.lis.ui.data.common.CachedDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
index 5e6d6b9c4648b97f08df34b06157aa72648c4903..ef855fbed83e5fd2dd6e916df7fa59f0ba01c26f 100644 (file)
@@ -2,6 +2,7 @@ package hu.user.lis.ui.data;
 
 import hu.user.lis.db.Supplier;
 import hu.user.lis.services.data.SupplierService;
+import hu.user.lis.ui.data.common.CachedDataModel;
 import lombok.extern.log4j.Log4j2;
 import org.apache.commons.lang3.StringUtils;
 import org.springframework.beans.factory.annotation.Autowired;
similarity index 99%
rename from lis-ui/src/main/java/hu/user/lis/ui/data/CachedDataModel.java
rename to lis-ui/src/main/java/hu/user/lis/ui/data/common/CachedDataModel.java
index 1610b48b9e564bd4b806625b7a7442f075ad71ca..8d07b1290a19297ad52e21c4bf4d1d5645797b41 100644 (file)
@@ -1,4 +1,4 @@
-package hu.user.lis.ui.data;
+package hu.user.lis.ui.data.common;
 
 import lombok.Setter;
 import lombok.extern.log4j.Log4j2;
similarity index 98%
rename from lis-ui/src/main/java/hu/user/lis/ui/data/CachedSpringDataModel.java
rename to lis-ui/src/main/java/hu/user/lis/ui/data/common/CachedSpringDataModel.java
index 8e000b98b0433a0f03ecee1257a4b407a83e8cee..ea47302c74294a8821d7984d173d930e900de203 100644 (file)
@@ -1,4 +1,4 @@
-package hu.user.lis.ui.data;
+package hu.user.lis.ui.data.common;
 
 import hu.user.lis.db.Profile;
 import hu.user.lis.services.data.EntityDataService;
similarity index 95%
rename from lis-ui/src/main/java/hu/user/lis/ui/data/FormDocument.java
rename to lis-ui/src/main/java/hu/user/lis/ui/data/common/FormDocument.java
index 8a7351a0c58f8b1512a689438069efe84eb03a20..ae2db8fd01221daae4b69eb41e5f316f0edf060e 100644 (file)
@@ -1,4 +1,4 @@
-package hu.user.lis.ui.data;
+package hu.user.lis.ui.data.common;
 
 import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.Builder;
@@ -10,7 +10,7 @@ import org.zkoss.lang.Strings;
 @Builder
 @Log4j2
 public class FormDocument extends JSONObject {
-    
+
     private Object data;
 
     public FormDocument setData(Object data) {
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/editor/ImportInvoiceApproveEditorModel.java b/lis-ui/src/main/java/hu/user/lis/ui/editor/ImportInvoiceApproveEditorModel.java
new file mode 100644 (file)
index 0000000..561ec5e
--- /dev/null
@@ -0,0 +1,59 @@
+package hu.user.lis.ui.editor;
+
+import com.google.common.collect.ImmutableMap;
+import hu.user.lis.db.Invoice;
+import hu.user.lis.db.Partner;
+import hu.user.lis.db.Project;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.log4j.Log4j2;
+import org.zkoss.bind.annotation.*;
+import org.zkoss.zk.ui.Component;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.Events;
+import org.zkoss.zul.Window;
+
+import java.util.Objects;
+
+@Log4j2
+@Getter
+@Setter
+public class ImportInvoiceApproveEditorModel extends InvoiceEditorModel {
+    @Init
+    public void init() {
+        super.init();
+    }
+
+    @Override
+    protected boolean canSave(Invoice entity) {
+        return super.canSave(entity)
+                && Objects.nonNull(entity.getProject())
+                && Objects.nonNull(entity.getFile());
+    }
+
+    @AfterCompose
+    public void onAfterCompose(@ContextParam(ContextType.VIEW) Component view) {
+        getEntitySelectorRouter().configureSelector(Project.class, getFormDocument(), "project");
+        getEntitySelectorRouter().configureSelector(Partner.class, getFormDocument(), "partner");
+        validate();
+    }
+
+    @Command
+    public void onCloseApproveWindow(@BindingParam("target") Window target, @BindingParam("save") int save) {
+        if (save == 0) {
+            Events.postEvent(new Event("onClose", target, null));
+            return;
+        }
+        ImmutableMap<String, Object> args = ImmutableMap.of("approved", save == 2, "modifiedEntity", getFormDocument());
+        if (save == 1) {
+            Events.postEvent(new Event("onClose", target,
+                    ImmutableMap.of("approved", false)));
+            return;
+        }
+
+        if (isSaveEnabled()) {
+            Events.postEvent(new Event("onClose", target,
+                    ImmutableMap.of("approved", true, "modifiedEntity", getFormDocument())));
+        }
+    }
+}
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/editor/ImportInvoiceAssignEditorModel.java b/lis-ui/src/main/java/hu/user/lis/ui/editor/ImportInvoiceAssignEditorModel.java
new file mode 100644 (file)
index 0000000..5864d73
--- /dev/null
@@ -0,0 +1,39 @@
+package hu.user.lis.ui.editor;
+
+import hu.user.lis.db.Invoice;
+import hu.user.lis.db.Partner;
+import hu.user.lis.db.Project;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.extern.log4j.Log4j2;
+import org.zkoss.bind.annotation.AfterCompose;
+import org.zkoss.bind.annotation.ContextParam;
+import org.zkoss.bind.annotation.ContextType;
+import org.zkoss.bind.annotation.Init;
+import org.zkoss.zk.ui.Component;
+
+import java.util.Objects;
+
+@Log4j2
+@Getter
+@Setter
+public class ImportInvoiceAssignEditorModel extends InvoiceEditorModel {
+    @Init
+    public void init() {
+        super.init();
+    }
+
+    @Override
+    protected boolean canSave(Invoice entity) {
+        return super.canSave(entity)
+                && Objects.nonNull(entity.getProject())
+                && Objects.nonNull(entity.getFile());
+    }
+
+    @AfterCompose
+    public void onAfterCompose(@ContextParam(ContextType.VIEW) Component view) {
+        getEntitySelectorRouter().configureSelector(Project.class, getFormDocument(), "project");
+        getEntitySelectorRouter().configureSelector(Partner.class, getFormDocument(), "partner");
+        validate();
+    }
+}
index 8bad8a113c8740e149843baaae9ceb22c4796197..098cf358227614a311dd89c49a8a8b439e70b75c 100644 (file)
@@ -29,6 +29,7 @@ public class InvoiceEditorModel extends EntityEditorModel<Invoice> {
 
     @Init
     public void init() {
+
         super.init();
     }
 
index d849a7ed715320b5439c04c9e7ad3a616941e712..6336065a7565bb1b2152dd6ba147f889c84e5548 100644 (file)
@@ -14,7 +14,8 @@ public class Editors {
     public static final String TREASURY = "~./editor/treasury-editor.zul";
     public static final String ASSOCIATE = "~./editor/associate-editor.zul";
     public static final String PARTNER = "~./editor/partner-editor.zul";
-
+    public static final String IMPORT_INVOICE_ASSIGN = "~./editor/import-invoice-assign-editor.zul";
+    public static final String IMPORT_INVOICE_APPROVE = "~./editor/import-invoice-approve-editor.zul";
     public static final String PROJECT = "~./editor/project-editor.zul";
 
     public static <E> void doEdit(String page, E entity, EditCompleted<E> editCompleted) {
index 9e7c80951b14b3ac301a60c05c6fc731cade59df..a1ffc4d42d69bc8c923c63f0b9cd3aaa41996ab3 100644 (file)
@@ -3,7 +3,7 @@ package hu.user.lis.ui.editor.selector;
 import hu.user.lis.db.Associate;
 import hu.user.lis.ui.converter.AssociateToNameConverter;
 import hu.user.lis.ui.data.AssociateSelectorDataModel;
-import hu.user.lis.ui.data.CachedSpringDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.log4j.Log4j2;
index 34014f429f40ae1c46f81f7992ad443f5ff67a93..21c8453cad79158b5d4a2d18e462049c58e5a3fe 100644 (file)
@@ -1,7 +1,7 @@
 package hu.user.lis.ui.editor.selector;
 
 import hu.user.lis.services.data.EntityDataServiceBase;
-import hu.user.lis.ui.data.CachedSpringDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.Getter;
 import lombok.extern.log4j.Log4j2;
 import org.springframework.beans.factory.annotation.Autowired;
index 474072064ae699b51cfd6bbf2ea28de28b106da9..d1bd41f0b997b789a2e15c8782829c66c5a7fd8a 100644 (file)
@@ -2,8 +2,8 @@ package hu.user.lis.ui.editor.selector;
 
 import hu.user.lis.db.Partner;
 import hu.user.lis.ui.converter.PartnerToNameConverter;
-import hu.user.lis.ui.data.CachedSpringDataModel;
 import hu.user.lis.ui.data.PartnerSelectorDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.log4j.Log4j2;
index d503837a06f064a41c70bf4caf3b5cd5d2bd256b..abdd007c2ca01bd35d74aa9a5beacc789933052c 100644 (file)
@@ -2,8 +2,8 @@ package hu.user.lis.ui.editor.selector;
 
 import hu.user.lis.db.Project;
 import hu.user.lis.ui.converter.ProjectToInfoConverter;
-import hu.user.lis.ui.data.CachedSpringDataModel;
 import hu.user.lis.ui.data.ProjectSelectorDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.log4j.Log4j2;
index 740bc245c368d262721340791b211754406c7cb9..431c47650c9e33ee648d58a0fcb46403d6fbd220 100644 (file)
@@ -11,6 +11,9 @@ import org.zkoss.zk.ui.event.EventQueues;
 @Service
 @Log4j2
 public class EventBus {
+    private EventQueue<Event> getProcessEventQueue() {
+        return EventQueues.lookup(Constants.PROCESS_EVENT_QUEUE, EventQueues.APPLICATION, true);
+    }
 
     private EventQueue<Event> getQueue() {
         return EventQueues.lookup(Constants.PROJECT_EDITOR_QUEUE, EventQueues.SESSION, true);
@@ -28,10 +31,15 @@ public class EventBus {
         getBindingQueue().subscribe(listener);
     }
 
+    public void registerForProcessEvent(EventListener listener) {
+        getProcessEventQueue().subscribe(listener);
+    }
+
     public void unregister(EventListener listener) {
         try {
             getQueue().unsubscribe(listener);
             getBindingQueue().unsubscribe(listener);
+            getProcessEventQueue().unsubscribe(listener);
         } catch (Exception e) {
 //            log.warn("Not necessary to unregister");
         }
@@ -60,4 +68,8 @@ public class EventBus {
     public void setProjectsListData(Object data) {
         getQueue().publish(new Event(Constants.SET_PROJECTS_LIST_DATA, null, data));
     }
+
+    public void refreshImportInvoiceTasks(String eventType) {
+        getProcessEventQueue().publish(new Event(Constants.REFRESH_INVOICE_IMPORT_TASKS, null, eventType));
+    }
 }
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/event/ProcessEventRouter.java b/lis-ui/src/main/java/hu/user/lis/ui/event/ProcessEventRouter.java
new file mode 100644 (file)
index 0000000..dc43e3b
--- /dev/null
@@ -0,0 +1,17 @@
+package hu.user.lis.ui.event;
+
+import hu.user.lis.workflow.event.ImportInvoicesEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationListener;
+import org.springframework.stereotype.Component;
+
+@Component
+public class ProcessEventRouter implements ApplicationListener<ImportInvoicesEvent> {
+    @Autowired
+    EventBus eventBus;
+
+    @Override
+    public void onApplicationEvent(ImportInvoicesEvent event) {
+        eventBus.refreshImportInvoiceTasks(event.getEventType());
+    }
+}
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/view/ApproveInvoicesViewModel.java b/lis-ui/src/main/java/hu/user/lis/ui/view/ApproveInvoicesViewModel.java
new file mode 100644 (file)
index 0000000..e51af8f
--- /dev/null
@@ -0,0 +1,100 @@
+package hu.user.lis.ui.view;
+
+import com.google.common.collect.ImmutableMap;
+import hu.user.lis.db.Invoice;
+import hu.user.lis.db.repository.InvoiceRepository;
+import hu.user.lis.ui.Constants;
+import hu.user.lis.ui.data.ApproveInvoicesDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
+import hu.user.lis.ui.editor.common.Editors;
+import hu.user.lis.ui.event.EventBus;
+import hu.user.lis.ui.event.ProcessEventRouter;
+import hu.user.lis.ui.view.common.EntityViewModel;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
+import org.zkoss.bind.BindUtils;
+import org.zkoss.bind.annotation.Command;
+import org.zkoss.bind.annotation.Init;
+import org.zkoss.bind.annotation.NotifyChange;
+import org.zkoss.json.JSONObject;
+import org.zkoss.zk.ui.Executions;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.EventListener;
+import org.zkoss.zk.ui.select.annotation.VariableResolver;
+import org.zkoss.zk.ui.select.annotation.WireVariable;
+import org.zkoss.zkplus.spring.DelegatingVariableResolver;
+import org.zkoss.zul.Window;
+
+import java.util.Map;
+import java.util.Objects;
+
+@Getter
+@Log4j2
+@VariableResolver(DelegatingVariableResolver.class)
+public class ApproveInvoicesViewModel extends EntityViewModel<JSONObject> implements EventListener<Event> {
+    @WireVariable
+    ApproveInvoicesDataModel approveInvoicesDataModel;
+
+    @WireVariable
+    ProcessEventRouter processEventRouter;
+
+    @WireVariable
+    InvoiceRepository invoiceRepository;
+
+    @WireVariable
+    EventBus eventBus;
+
+    @Override
+    protected CachedSpringDataModel<JSONObject> getDataModel() {
+        return approveInvoicesDataModel;
+    }
+
+    @Override
+    protected String getNavigation() {
+        return Constants.NAV_IMPORT_INVOICES_APPROVE;
+    }
+
+    @Init
+    public void init() {
+        super.init();
+    }
+
+    @Command
+    @NotifyChange("approveInvoicesDataModel")
+    public void onRefresh() {
+        setSelectedEntity(null);
+        approveInvoicesDataModel.refresh();
+        BindUtils.postNotifyChange(this, "selectedEntity");
+        BindUtils.postNotifyChange(this, "approveInvoicesDataModel");
+    }
+
+    @Command
+    public void onHandleTask() {
+        Invoice entity = (Invoice) getSelectedEntity().get("invoiceEntity");
+        ImmutableMap<String, Object> args = ImmutableMap.of("formDocument", entity);
+        Window editorWindow = (Window) Executions.createComponents(Editors.IMPORT_INVOICE_APPROVE, null, args);
+        editorWindow.addEventListener("onClose", e -> {
+            if (Objects.nonNull(e.getData())) {
+                Map<String, Object> results = (Map<String, Object>) e.getData();
+                boolean approved = (boolean) results.get("approved");
+                Invoice modifiedEntity = (Invoice) results.get("modifiedEntity");
+                //TODO move entity persistance to BPMN
+                if (approved) {
+                    invoiceRepository.save(modifiedEntity);
+                }
+                approveInvoicesDataModel.completeTask(getSelectedEntity(), results);
+                onRefresh();
+            }
+        });
+        editorWindow.doModal();
+        editorWindow.setFocus(true);
+    }
+
+    @Override
+    public void onEvent(Event event) throws Exception {
+        if (Constants.REFRESH_INVOICE_IMPORT_TASKS.equals(event.getName())) {
+            onRefresh();
+            log.info("Refreshed by {}", event.getData());
+        }
+    }
+}
diff --git a/lis-ui/src/main/java/hu/user/lis/ui/view/AssignInvoicesViewModel.java b/lis-ui/src/main/java/hu/user/lis/ui/view/AssignInvoicesViewModel.java
new file mode 100644 (file)
index 0000000..ae2186e
--- /dev/null
@@ -0,0 +1,87 @@
+package hu.user.lis.ui.view;
+
+import com.google.common.collect.ImmutableMap;
+import hu.user.lis.db.Invoice;
+import hu.user.lis.ui.Constants;
+import hu.user.lis.ui.data.ImportInvoicesDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
+import hu.user.lis.ui.editor.common.Editors;
+import hu.user.lis.ui.event.EventBus;
+import hu.user.lis.ui.event.ProcessEventRouter;
+import hu.user.lis.ui.view.common.EntityViewModel;
+import hu.user.lis.workflow.invoice.service.WorkflowManagerService;
+import lombok.Getter;
+import lombok.extern.log4j.Log4j2;
+import org.zkoss.bind.BindUtils;
+import org.zkoss.bind.annotation.Command;
+import org.zkoss.bind.annotation.Destroy;
+import org.zkoss.bind.annotation.Init;
+import org.zkoss.json.JSONObject;
+import org.zkoss.zk.ui.event.Event;
+import org.zkoss.zk.ui.event.EventListener;
+import org.zkoss.zk.ui.select.annotation.VariableResolver;
+import org.zkoss.zk.ui.select.annotation.WireVariable;
+import org.zkoss.zkplus.spring.DelegatingVariableResolver;
+
+@Getter
+@Log4j2
+@VariableResolver(DelegatingVariableResolver.class)
+public class AssignInvoicesViewModel extends EntityViewModel<JSONObject> implements EventListener<Event> {
+    @WireVariable
+    ImportInvoicesDataModel importInvoicesDataModel;
+
+    @WireVariable
+    ProcessEventRouter processEventRouter;
+
+    @WireVariable
+    EventBus eventBus;
+
+    @WireVariable
+    WorkflowManagerService workflowManagerService;
+
+    @Override
+    protected CachedSpringDataModel<JSONObject> getDataModel() {
+        return importInvoicesDataModel;
+    }
+
+    @Override
+    protected String getNavigation() {
+        return Constants.NAV_IMPORT_INVOICES_ASSIGN;
+    }
+
+    @Init
+    public void init() {
+        super.init();
+        eventBus.registerForProcessEvent(this);
+    }
+
+    @Command
+    public void onRefresh() {
+        setSelectedEntity(null);
+        importInvoicesDataModel.refresh();
+        BindUtils.postNotifyChange(this, "selectedEntity");
+        BindUtils.postNotifyChange(this, "importInvoicesDataModel");
+    }
+
+    @Command
+    public void onHandleTask() {
+        Invoice entity = (Invoice) getSelectedEntity().get("invoiceEntity");
+        Editors.doEdit(Editors.IMPORT_INVOICE_ASSIGN, entity, modifiedEntity -> {
+            importInvoicesDataModel.completeTask(getSelectedEntity(), ImmutableMap.of("invoiceEntity", modifiedEntity));
+            onRefresh();
+        });
+    }
+
+    @Override
+    public void onEvent(Event event) throws Exception {
+        if (Constants.REFRESH_INVOICE_IMPORT_TASKS.equals(event.getName())) {
+            onRefresh();
+            log.info("Refreshed by {}", event.getData());
+        }
+    }
+
+    @Destroy
+    public void onDestroy() {
+        eventBus.unregister(this);
+    }
+}
index 1666fc8915b3b5ca8842a935c2af476b0136df6e..b7c86ebb893a963a57e731f93614a7c996e4695f 100644 (file)
@@ -4,7 +4,7 @@ import com.google.common.collect.ImmutableMap;
 import hu.user.lis.db.Associate;
 import hu.user.lis.ui.Constants;
 import hu.user.lis.ui.data.AssociatesDataModel;
-import hu.user.lis.ui.data.CachedSpringDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import hu.user.lis.ui.editor.common.Editors;
 import hu.user.lis.ui.view.common.FilterActiveViewModel;
 import lombok.Getter;
@@ -14,8 +14,8 @@ import org.zkoss.bind.annotation.Init;
 import org.zkoss.zk.ui.select.annotation.WireVariable;
 import org.zkoss.zul.Messagebox;
 
-import static hu.user.lis.ui.data.CachedDataModel.ASCENDING;
-import static hu.user.lis.ui.data.CachedDataModel.NATURAL;
+import static hu.user.lis.ui.data.common.CachedDataModel.ASCENDING;
+import static hu.user.lis.ui.data.common.CachedDataModel.NATURAL;
 
 @Log4j2
 public class AssociatesViewModel extends FilterActiveViewModel<Associate> {
index e3a35fe5cd64a2a3c92ea18e4d9ae4c240e14f64..d87f5bbf468d0cca21fb320f2624ded2caea86d4 100644 (file)
@@ -1,11 +1,11 @@
 package hu.user.lis.ui.view;
 
-import com.google.common.collect.ImmutableMap;
 import hu.user.lis.ui.Constants;
 import hu.user.lis.ui.auth.CurrentProfile;
 import hu.user.lis.ui.editor.common.Editors;
 import hu.user.lis.ui.event.EventBus;
 import hu.user.lis.ui.session.SessionSettings;
+import hu.user.lis.workflow.invoice.service.WorkflowManagerService;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.log4j.Log4j2;
@@ -19,30 +19,37 @@ import org.zkoss.zk.ui.event.Event;
 import org.zkoss.zk.ui.event.EventListener;
 import org.zkoss.zk.ui.select.annotation.VariableResolver;
 import org.zkoss.zk.ui.select.annotation.WireVariable;
-
-import java.util.Map;
+import org.zkoss.zk.ui.util.Notification;
 
 
 @Log4j2
 @VariableResolver(org.zkoss.zkplus.spring.DelegatingVariableResolver.class)
 @Getter
 @Setter
-public class IndexViewModel implements EventListener {
-    private static final String PARTNERS_LIST = "~./partners.zul";
+public class IndexViewModel implements EventListener<Event> {
+    //    private static final String PARTNERS_LIST = "~./partners.zul";
     private static final String PROJECTS_LIST = "~./projects.zul";
-    private static final String ASSOCIATES_LIST = "~./associates.zul";
-    private static final String PROJECT_ASSOCIATES_LIST = "~./project-associates.zul";
+    //    private static final String ASSOCIATES_LIST = "~./associates.zul";
+//    private static final String PROJECT_ASSOCIATES_LIST = "~./project-associates.zul";
     private static final String SERVICE_RECORDS_LIST = "~./service-records.zul";
-    private static final String SETTINGS_LIST = "~./settings.zul";
-    public static Map<String, String> NAVIGATION = ImmutableMap.of(
-            Constants.NAV_PARTNERS, PARTNERS_LIST,
-            Constants.NAV_PROJECTS, PROJECTS_LIST,
-            Constants.NAV_PROJECT, Editors.PROJECT,
-            Constants.NAV_ASSOCIATES, ASSOCIATES_LIST,
-            Constants.NAV_PROJECT_ASSOCIATES, PROJECT_ASSOCIATES_LIST,
-            Constants.NAV_SERVICE_RECORDS, SERVICE_RECORDS_LIST,
-            Constants.NAV_SETTINGS, SETTINGS_LIST
-    );
+//    private static final String SETTINGS_LIST = "~./settings.zul";
+//    private static final String IMPORT_INVOICES_LIST = "~./import-invoices-assign.zul";
+//    private static final String APPROVE_INVOICES_LIST = "~./import-invoices-approve.zul";
+//
+//    public static Map<String, String> NAVIGATION = ImmutableMap.of(
+//            Constants.NAV_PARTNERS, PARTNERS_LIST,
+//            Constants.NAV_PROJECTS, PROJECTS_LIST,
+//            Constants.NAV_PROJECT, Editors.PROJECT,
+//            Constants.NAV_ASSOCIATES, ASSOCIATES_LIST,
+//            Constants.NAV_PROJECT_ASSOCIATES, PROJECT_ASSOCIATES_LIST,
+//            Constants.NAV_SERVICE_RECORDS, SERVICE_RECORDS_LIST,
+//            Constants.NAV_SETTINGS, SETTINGS_LIST,
+//            Constants.NAV_IMPORT_INVOICES, IMPORT_INVOICES_LIST,
+//            Constants.NAV_APPROVE_INVOICES, IMPORT_INVOICES_LIST
+//    );
+
+    @WireVariable
+    EventBus eventBus;
 
     @WireVariable
     BuildProperties buildProperties;
@@ -51,10 +58,10 @@ public class IndexViewModel implements EventListener {
     SessionSettings sessionSettings;
 
     @WireVariable
-    EventBus eventBus;
+    CurrentProfile currentProfile;
 
     @WireVariable
-    CurrentProfile currentProfile;
+    WorkflowManagerService workflowManagerService;
 
     String searchPhrase;
 
@@ -70,16 +77,14 @@ public class IndexViewModel implements EventListener {
     }
 
     private void route(String path) {
-        if (path.startsWith("/project/") && path.split("/").length > 2) {
+        if (path.startsWith(Constants.NAV_PROJECTS + "/") && path.split("/").length > 2) {
             String id = path.split("/")[2];
             selectPage(Editors.PROJECT);
             eventBus.setProjectEditorData(Long.parseLong(id));
         } else {
-            if (NAVIGATION.containsKey(path)) {
-                setPage(NAVIGATION.get(path));
-            } else {
-                setPage(PARTNERS_LIST);
-            }
+            if (Constants.NAV_ROOT.equals(path))
+                path = Constants.NAV_PARTNERS;
+            setPage("~." + path + ".zul");
         }
     }
 
@@ -110,7 +115,20 @@ public class IndexViewModel implements EventListener {
 
     @Destroy
     public void onDestroy() {
-        log.info("Destroy {}");
         eventBus.unregister(this);
     }
+
+    @Command
+    public void onStartImportInvoiceProcess() {
+        try {
+            workflowManagerService.startImportIncomingInvoicesProcess();
+        } catch (Exception e) {
+            Notification.show(e.getMessage(), true);
+        }
+    }
+
+    @Command
+    public void onCancelImportInvoiceProcess() {
+        workflowManagerService.cancelInvoiceImportProcess();
+    }
 }
index 5f84d128f3744be27ed9e700e670ccd84f8520ad..a6a5208c0211d86ad20f0e12abd43cabdbe7bbb4 100644 (file)
@@ -3,8 +3,8 @@ package hu.user.lis.ui.view;
 import com.google.common.collect.ImmutableMap;
 import hu.user.lis.db.Partner;
 import hu.user.lis.ui.Constants;
-import hu.user.lis.ui.data.CachedSpringDataModel;
 import hu.user.lis.ui.data.PartnersDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import hu.user.lis.ui.editor.common.Editors;
 import hu.user.lis.ui.view.common.FilterActiveViewModel;
 import lombok.Getter;
@@ -14,8 +14,8 @@ import org.zkoss.bind.annotation.Init;
 import org.zkoss.zk.ui.select.annotation.WireVariable;
 import org.zkoss.zul.Messagebox;
 
-import static hu.user.lis.ui.data.CachedDataModel.ASCENDING;
-import static hu.user.lis.ui.data.CachedDataModel.NATURAL;
+import static hu.user.lis.ui.data.common.CachedDataModel.ASCENDING;
+import static hu.user.lis.ui.data.common.CachedDataModel.NATURAL;
 
 @Log4j2
 public class PartnersViewModel extends FilterActiveViewModel<Partner> {
index 125b9009556c85c0959c429b3a5894333952ee97..0703ce0dc3ed694caad0d81149535544400caa01 100644 (file)
@@ -3,8 +3,8 @@ package hu.user.lis.ui.view;
 import com.google.common.collect.ImmutableMap;
 import hu.user.lis.db.Project;
 import hu.user.lis.ui.Constants;
-import hu.user.lis.ui.data.CachedSpringDataModel;
 import hu.user.lis.ui.data.ProjectsDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import hu.user.lis.ui.event.EventBus;
 import hu.user.lis.ui.view.common.FilterActiveViewModel;
 import lombok.Getter;
@@ -20,8 +20,8 @@ import org.zkoss.zul.Messagebox;
 import java.util.Map;
 import java.util.Objects;
 
-import static hu.user.lis.ui.data.CachedDataModel.ASCENDING;
-import static hu.user.lis.ui.data.CachedDataModel.NATURAL;
+import static hu.user.lis.ui.data.common.CachedDataModel.ASCENDING;
+import static hu.user.lis.ui.data.common.CachedDataModel.NATURAL;
 
 
 @Log4j2
index 3fc73f3b6697df7f652f5b31569ce948523c0dc7..6b78af6c1547c6b3ace6387f11b39a0837f2269c 100644 (file)
@@ -5,8 +5,8 @@ import hu.user.lis.db.Associate;
 import hu.user.lis.db.Project;
 import hu.user.lis.db.ServiceRecord;
 import hu.user.lis.ui.Constants;
-import hu.user.lis.ui.data.CachedSpringDataModel;
 import hu.user.lis.ui.data.ServiceRecordsDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import hu.user.lis.ui.editor.common.Editors;
 import hu.user.lis.ui.editor.selector.EntitySelectorRouter;
 import hu.user.lis.ui.event.EventBus;
@@ -27,8 +27,8 @@ import org.zkoss.zkplus.spring.DelegatingVariableResolver;
 import java.util.Map;
 import java.util.Objects;
 
-import static hu.user.lis.ui.data.CachedDataModel.ASCENDING;
-import static hu.user.lis.ui.data.CachedDataModel.NATURAL;
+import static hu.user.lis.ui.data.common.CachedDataModel.ASCENDING;
+import static hu.user.lis.ui.data.common.CachedDataModel.NATURAL;
 
 
 @Log4j2
index 578cad2b7c3f45ea0b3197747bd721d2b61f7f1b..21d319a057b998e8ba4aa552916f5b3011a35f35 100644 (file)
@@ -1,7 +1,7 @@
 package hu.user.lis.ui.view.common;
 
 import hu.user.lis.services.data.EntityDataService;
-import hu.user.lis.ui.data.CachedSpringDataModel;
+import hu.user.lis.ui.data.common.CachedSpringDataModel;
 import lombok.Getter;
 import lombok.Setter;
 import lombok.extern.log4j.Log4j2;
diff --git a/lis-ui/src/main/resources/web/editor/import-invoice-approve-editor.zul b/lis-ui/src/main/resources/web/editor/import-invoice-approve-editor.zul
new file mode 100644 (file)
index 0000000..ee110e0
--- /dev/null
@@ -0,0 +1,122 @@
+<?link rel="stylesheet" type="text/css" href="~./static/css/skeleton.css" ?>
+<?link rel="stylesheet" type="text/css" href="~./static/css/webclient.css" ?>
+<?component name="entity-selector" inline="true" class="hu.user.lis.ui.editor.widget.EntitySelector"?>
+<zk>
+    <zscript>
+        import hu.user.lis.db.Currency;
+        ListModelList currencies = new ListModelList(Currency.values());
+    </zscript>
+    <window id="invoiceApprovePopup" width="50%" height="400px" closable="true"
+            maximizable="true" sizable="true"
+            viewModel="@id('vm') @init('hu.user.lis.ui.editor.ImportInvoiceApproveEditorModel')">
+        <caption label="Számla import - Jóváhagyás"/>
+        <borderlayout>
+            <center border="none" vflex="true" hflex="true">
+                <tabbox vflex="true" hflex="true">
+                    <tabs>
+                        <tab label="Adatok" selected="true"/>
+                        <tab label="Számlakép"/>
+                    </tabs>
+                    <tabpanels>
+                        <tabpanel>
+                            <vlayout hflex="true">
+                                <label value="Projekt"/>
+                                <entity-selector entity="Project" hflex="true"/>
+
+                                <label value="Leírás"/>
+                                <textbox hflex="true" instant="true"
+                                         value="@bind(vm.formDocument.title) @validator(vm)"
+                                         forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                <label value="Partner"/>
+                                <entity-selector entity="Partner"/>
+                                <hlayout>
+                                    <vlayout>
+                                        <label value="Sorszám"/>
+                                        <textbox instant="true" value="@bind(vm.formDocument.humanId) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Pénznem"/>
+                                        <combobox instant="true" model="${currencies}"
+                                                  selectedItem="@bind(vm.formDocument.currency) @validator(vm)"
+                                                  onChange="@command('onNetAmountChange')"
+                                                  forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Nettó összeg"/>
+                                        <doublebox value="@bind(vm.formDocument.netAmount) @validator(vm)"
+                                                   format="#,###.##" locale="hu" instant="true"
+                                                   onChange="@command('onNetAmountChange')"
+                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Bruttó összeg"/>
+                                        <doublebox value="@bind(vm.formDocument.grossAmount) @validator(vm)"
+                                                   format="#,###.##" locale="hu" instant="true"
+                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="ÁFA"/>
+                                        <doublebox value="@bind(vm.formDocument.vatAmount) @validator(vm)"
+                                                   format="#,###.##" locale="hu" instant="true"
+                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                </hlayout>
+                                <hlayout>
+                                    <vlayout>
+                                        <label value="Kiállítás dátuma"/>
+                                        <datebox instant="true" format="yyyy. MM. dd."
+                                                 value="@bind(vm.formDocument.createDate) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Teljesítés dátuma"/>
+                                        <datebox instant="true" format="yyyy. MM. dd."
+                                                 value="@bind(vm.formDocument.completionDate) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Fizetési határidő"/>
+                                        <datebox instant="true" format="yyyy. MM. dd."
+                                                 value="@bind(vm.formDocument.paymentDeadline) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                </hlayout>
+                            </vlayout>
+                        </tabpanel>
+                        <tabpanel>
+                            <borderlayout>
+                                <north flex="true">
+                                    <toolbar>
+                                        <toolbarbutton label="Feltöltés" iconSclass="z-icon-plus" upload="true"
+                                                       onUpload="@command('onUploadFile')"/>
+                                        <toolbarbutton label="Törlés" iconSclass="z-icon-remove"
+                                                       onClick="@command('onRemoveFile')"
+                                                       disabled="@load(empty vm.formDocument.file)"/>
+                                    </toolbar>
+                                </north>
+                                <center border="none" flex="true">
+                                    <iframe hflex="true" vflex="true"
+                                            content="@load(vm.formDocument.file) @converter('hu.user.lis.ui.converter.ByteArrayToAMediaConverter')"/>
+                                </center>
+                            </borderlayout>
+                        </tabpanel>
+                    </tabpanels>
+                </tabbox>
+
+            </center>
+            <south border="none" flex="true" style="text-align: right; padding: 10px;">
+                <hlayout>
+                    <button id="cancel" label="Bezár"
+                            onClick="@command('onCloseApproveWindow', target=invoiceApprovePopup, save=0)"/>
+                    <button id="reject" label="Elutasít" style="border-color: red"
+                            onClick="@command('onCloseApproveWindow', target=invoiceApprovePopup, save=1)"/>
+                    <button id="submit" label="Jóváhagy" style="border-color: green"
+                            onClick="@command('onCloseApproveWindow', target=invoiceApprovePopup, save=2)"
+                            disabled="@bind(not vm.saveEnabled)"/>
+                </hlayout>
+            </south>
+        </borderlayout>
+    </window>
+</zk>
diff --git a/lis-ui/src/main/resources/web/editor/import-invoice-assign-editor.zul b/lis-ui/src/main/resources/web/editor/import-invoice-assign-editor.zul
new file mode 100644 (file)
index 0000000..ab121d6
--- /dev/null
@@ -0,0 +1,120 @@
+<?link rel="stylesheet" type="text/css" href="~./static/css/skeleton.css" ?>
+<?link rel="stylesheet" type="text/css" href="~./static/css/webclient.css" ?>
+<?component name="entity-selector" inline="true" class="hu.user.lis.ui.editor.widget.EntitySelector"?>
+<zk>
+    <zscript>
+        import hu.user.lis.db.Currency;
+        ListModelList currencies = new ListModelList(Currency.values());
+    </zscript>
+    <window id="invoiceAssignPopup" width="50%" height="400px" closable="true"
+            maximizable="true" sizable="true"
+            viewModel="@id('vm') @init('hu.user.lis.ui.editor.ImportInvoiceAssignEditorModel')">
+        <caption label="Számla import - Csatolás"/>
+        <borderlayout>
+            <center border="none" vflex="true" hflex="true">
+                <tabbox vflex="true" hflex="true">
+                    <tabs>
+                        <tab label="Adatok" selected="true"/>
+                        <tab label="Számlakép"/>
+                    </tabs>
+                    <tabpanels>
+                        <tabpanel>
+                            <vlayout hflex="true">
+                                <label value="Projekt"/>
+                                <entity-selector entity="Project" hflex="true"/>
+
+                                <label value="Leírás"/>
+                                <textbox hflex="true" instant="true"
+                                         value="@bind(vm.formDocument.title) @validator(vm)"
+                                         forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                <label value="Partner"/>
+                                <entity-selector entity="Partner"/>
+                                <hlayout>
+                                    <vlayout>
+                                        <label value="Sorszám"/>
+                                        <textbox instant="true" value="@bind(vm.formDocument.humanId) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Pénznem"/>
+                                        <combobox instant="true" model="${currencies}"
+                                                  selectedItem="@bind(vm.formDocument.currency) @validator(vm)"
+                                                  onChange="@command('onNetAmountChange')"
+                                                  forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Nettó összeg"/>
+                                        <doublebox value="@bind(vm.formDocument.netAmount) @validator(vm)"
+                                                   format="#,###.##" locale="hu" instant="true"
+                                                   onChange="@command('onNetAmountChange')"
+                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Bruttó összeg"/>
+                                        <doublebox value="@bind(vm.formDocument.grossAmount) @validator(vm)"
+                                                   format="#,###.##" locale="hu" instant="true"
+                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="ÁFA"/>
+                                        <doublebox value="@bind(vm.formDocument.vatAmount) @validator(vm)"
+                                                   format="#,###.##" locale="hu" instant="true"
+                                                   forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                </hlayout>
+                                <hlayout>
+                                    <vlayout>
+                                        <label value="Kiállítás dátuma"/>
+                                        <datebox instant="true" format="yyyy. MM. dd."
+                                                 value="@bind(vm.formDocument.createDate) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Teljesítés dátuma"/>
+                                        <datebox instant="true" format="yyyy. MM. dd."
+                                                 value="@bind(vm.formDocument.completionDate) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                    <vlayout>
+                                        <label value="Fizetési határidő"/>
+                                        <datebox instant="true" format="yyyy. MM. dd."
+                                                 value="@bind(vm.formDocument.paymentDeadline) @validator(vm)"
+                                                 forward="onOK=submit.onClick, onCancel=cancel.onClick"/>
+                                    </vlayout>
+                                </hlayout>
+                            </vlayout>
+                        </tabpanel>
+                        <tabpanel>
+                            <borderlayout>
+                                <north flex="true">
+                                    <toolbar>
+                                        <toolbarbutton label="Feltöltés" iconSclass="z-icon-plus" upload="true"
+                                                       onUpload="@command('onUploadFile')"/>
+                                        <toolbarbutton label="Törlés" iconSclass="z-icon-remove"
+                                                       onClick="@command('onRemoveFile')"
+                                                       disabled="@load(empty vm.formDocument.file)"/>
+                                    </toolbar>
+                                </north>
+                                <center border="none" flex="true">
+                                    <iframe hflex="true" vflex="true"
+                                            content="@load(vm.formDocument.file) @converter('hu.user.lis.ui.converter.ByteArrayToAMediaConverter')"/>
+                                </center>
+                            </borderlayout>
+                        </tabpanel>
+                    </tabpanels>
+                </tabbox>
+
+            </center>
+            <south border="none" flex="true" style="text-align: right; padding: 10px;">
+                <hlayout>
+                    <button id="cancel" label="Bezár"
+                            onClick="@command('onCloseWindow', target=invoiceAssignPopup, save=false)"/>
+                    <button id="submit" label="Mentés"
+                            onClick="@command('onCloseWindow', target=invoiceAssignPopup, save=true)"
+                            disabled="@bind(not vm.saveEnabled)"/>
+                </hlayout>
+            </south>
+        </borderlayout>
+    </window>
+</zk>
diff --git a/lis-ui/src/main/resources/web/import-invoices-approve.zul b/lis-ui/src/main/resources/web/import-invoices-approve.zul
new file mode 100644 (file)
index 0000000..68a1623
--- /dev/null
@@ -0,0 +1,33 @@
+<zk>
+    <window vflex="true" viewModel="@id('vm') @init('hu.user.lis.ui.view.ApproveInvoicesViewModel')">
+        <!--        <timer id="timer" delay="1000" repeats="true" onTimer="@command('uiTick')"/>-->
+        <caption label="Számla import - Jóváhagyás"/>
+
+        <borderlayout>
+            <north flex="true">
+                <toolbar>
+                    <toolbarbutton label="Frissít" iconSclass="z-icon-refresh"
+                                   onClick="@command('onRefresh')"/>
+                    <!--                    <toolbarbutton label="Léptet" iconSclass="z-icon-tasks"-->
+                    <!--                                   onClick="@command('onCompleteTask')" disabled="@load(empty vm.selectedEntity)"/>-->
+                </toolbar>
+            </north>
+            <center border="none" flex="true">
+                <listbox id="importInvoicesList" vflex="true" model="@load(vm.approveInvoicesDataModel)"
+                         autopaging="true" mold="paging" pagingPosition="top" multiple="false"
+                         onSelect="@command('onListSelection')" onDoubleClick="@command('onHandleTask')">
+                    <listhead sizable="true">
+                        <listheader label="Azonosító" sort="auto(humanId)" align="left"/>
+                        <listheader label="Partner" sort="auto(partner.name)" align="left"/>
+                    </listhead>
+                    <template name="model">
+                        <listitem>
+                            <listcell label="@load(each['invoiceEntity'].humanId)"/>
+                            <listcell label="@load(each['invoiceEntity'].partner.name)"/>
+                        </listitem>
+                    </template>
+                </listbox>
+            </center>
+        </borderlayout>
+    </window>
+</zk>
\ No newline at end of file
diff --git a/lis-ui/src/main/resources/web/import-invoices-assign.zul b/lis-ui/src/main/resources/web/import-invoices-assign.zul
new file mode 100644 (file)
index 0000000..0c6996c
--- /dev/null
@@ -0,0 +1,33 @@
+<zk>
+    <window vflex="true" viewModel="@id('vm') @init('hu.user.lis.ui.view.AssignInvoicesViewModel')">
+        <!--        <timer id="timer" delay="1000" repeats="true" onTimer="@command('uiTick')"/>-->
+        <caption label="Számla import - Csatolás"/>
+
+        <borderlayout>
+            <north flex="true">
+                <toolbar>
+                    <toolbarbutton label="Frissít" iconSclass="z-icon-refresh"
+                                   onClick="@command('onRefresh')"/>
+                    <!--                    <toolbarbutton label="Léptet" iconSclass="z-icon-tasks"-->
+                    <!--                                   onClick="@command('onHandleTask')" disabled="@load(empty vm.selectedEntity)"/>-->
+                </toolbar>
+            </north>
+            <center border="none" flex="true">
+                <listbox id="importInvoicesList" vflex="true" model="@load(vm.importInvoicesDataModel)"
+                         autopaging="true" mold="paging" pagingPosition="top" multiple="false"
+                         onSelect="@command('onListSelection')" onDoubleClick="@command('onHandleTask')">
+                    <listhead sizable="true">
+                        <listheader label="Azonosító" sort="auto(humanId)" align="left"/>
+                        <listheader label="Partner" sort="auto(partner.name)" align="left"/>
+                    </listhead>
+                    <template name="model">
+                        <listitem>
+                            <listcell label="@load(each['invoiceEntity'].humanId)"/>
+                            <listcell label="@load(each['invoiceEntity'].partner.name)"/>
+                        </listitem>
+                    </template>
+                </listbox>
+            </center>
+        </borderlayout>
+    </window>
+</zk>
\ No newline at end of file
index a7bbdd8528852ec819180d65cc6d10c8f3660ab7..ce1ae9431ec3b71e11b3d674bb502e88301f3798 100644 (file)
                             <menuseparator/>
                             <menuitem iconSclass="z-icon-user" label="Munkatársak"
                                       onClick="@command(vm.selectPage('~./associates.zul'))"/>
+                            <menu iconSclass="z-icon-tasks" label="Számla import">
+                                <menupopup>
+                                    <menuitem iconSclass="z-icon-paperclip" label="Csatolás"
+                                              onClick="@command(vm.selectPage('~./import-invoices-assign.zul'))"/>
+                                    <menuitem iconSclass="z-icon-check-square-o" label="Jóváhagyás"
+                                              onClick="@command(vm.selectPage('~./import-invoices-approve.zul'))"/>
+                                    <menuseparator/>
+                                    <menuitem iconSclass="z-icon-code-fork" label="Import elindítása"
+                                              onClick="@command('onStartImportInvoiceProcess')"/>
+                                    <menuitem iconSclass="z-icon-times-circle-o" label="Import leállítása"
+                                              onClick="@command('onCancelImportInvoiceProcess')"/>
+                                </menupopup>
+                            </menu>
                             <menuseparator/>
                             <menuitem iconSclass="z-icon-cogs" label="Beállítások"
                                       onClick="@command(vm.selectPage('~./settings.zul'))"/>
+                            <!--                            <menuitem iconSclass="z-icon-cogs" label="Camunda"-->
+                            <!--                                      onClick="@command(vm.selectPage('/camunda'))"/>-->
                         </menubar>
                         <hbox hflex="min" pack="right">
                             <textbox value="@bind(vm.searchPhrase)" onOK="@command('search')" disabled="true"></textbox>
index 5ae50fa8a1f9a84b7970140641b37b0e9e67224c..18f808bc225aeb0dc780421bcbc112ae1899e2cd 100644 (file)
@@ -44,5 +44,9 @@
             <artifactId>camunda-bpm-spring-boot-starter-rest</artifactId>
             <version>${camunda.spring-boot.version}</version>
         </dependency>
+        <dependency>
+            <groupId>org.springframework</groupId>
+            <artifactId>spring-context</artifactId>
+        </dependency>
     </dependencies>
 </project>
diff --git a/lis-workflow/src/main/java/hu/user/lis/workflow/event/ImportInvoicesEvent.java b/lis-workflow/src/main/java/hu/user/lis/workflow/event/ImportInvoicesEvent.java
new file mode 100644 (file)
index 0000000..1a40a0f
--- /dev/null
@@ -0,0 +1,7 @@
+package hu.user.lis.workflow.event;
+
+public class ImportInvoicesEvent extends ProcessEvent {
+    public ImportInvoicesEvent(Object source) {
+        super(source);
+    }
+}
diff --git a/lis-workflow/src/main/java/hu/user/lis/workflow/event/ProcessEvent.java b/lis-workflow/src/main/java/hu/user/lis/workflow/event/ProcessEvent.java
new file mode 100644 (file)
index 0000000..ed4350f
--- /dev/null
@@ -0,0 +1,19 @@
+package hu.user.lis.workflow.event;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.context.ApplicationEvent;
+
+@Getter
+@Setter
+public class ProcessEvent extends ApplicationEvent {
+
+    private String name;
+    private String key;
+    private String id;
+    private String eventType;
+
+    public ProcessEvent(Object source) {
+        super(source);
+    }
+}
index b9e1730905ea751024eaf90f439843a2ef079cd6..9e5415b85db20dc0779830352f48ed05b419ccf4 100644 (file)
@@ -1,40 +1,32 @@
 package hu.user.lis.workflow.invoice;
 
-import hu.user.lis.db.Invoice;
-import hu.user.lis.db.Partner;
+import hu.user.lis.db.IncomingInvoice;
 import hu.user.lis.db.repository.InvoiceRepository;
-import hu.user.lis.db.repository.PartnerRepository;
+import hu.user.lis.workflow.invoice.service.IncomingInvoiceFetcherService;
 import lombok.extern.log4j.Log4j2;
-import org.apache.commons.lang3.RandomUtils;
 import org.camunda.bpm.engine.delegate.DelegateExecution;
 import org.camunda.bpm.engine.delegate.JavaDelegate;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.util.List;
-
 @Log4j2
 @Component
 public class DownloadInvoiceData implements JavaDelegate {
     @Autowired
     InvoiceRepository invoiceRepository;
+
     @Autowired
-    PartnerRepository partnerRepository;
+    IncomingInvoiceFetcherService incomingInvoiceFetcherService;
+
 
     @Override
     public void execute(DelegateExecution delegateExecution) throws Exception {
-        String invoiceHumanId = (String) delegateExecution.getVariableLocal("invoice");
-        log.info("Processing incoice {}", invoiceHumanId);
-        Invoice invoice = Invoice.builder()
-                .humanId(invoiceHumanId)
-                .partner(getRandomPartner())
-                .build();
+        String humanId = (String) delegateExecution.getVariable("invoice");
+        log.info("Processing invoice {}", humanId);
+        IncomingInvoice invoice = incomingInvoiceFetcherService.getInvoice(humanId);
         invoiceRepository.save(invoice);
-        log.info("Invoice {} processed", invoiceHumanId);
+        delegateExecution.setVariableLocal("invoiceEntity", invoice);
+        log.info("Invoice {} processed", humanId);
     }
 
-    private Partner getRandomPartner() {
-        List<Partner> partners = partnerRepository.findAll();
-        return partners.get(RandomUtils.nextInt(0, partners.size()));
-    }
 }
index 0f91876a501cb4e938f66b7e470f59216876240d..e07822e8e4d25de0bb659bdce3fd4c88ffbf01e7 100644 (file)
@@ -1,20 +1,24 @@
 package hu.user.lis.workflow.invoice;
 
+import hu.user.lis.workflow.invoice.service.IncomingInvoiceFetcherService;
 import lombok.extern.log4j.Log4j2;
 import org.camunda.bpm.engine.delegate.DelegateExecution;
 import org.camunda.bpm.engine.delegate.JavaDelegate;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
-import java.util.Arrays;
 import java.util.List;
 
 @Log4j2
 @Component
 public class ListNewInvoices implements JavaDelegate {
+    @Autowired
+    IncomingInvoiceFetcherService incomingInvoiceFetcherService;
+
     @Override
     public void execute(DelegateExecution delegateExecution) throws Exception {
         log.info("Executing");
-        List<String> invoices = Arrays.asList("Invoice-test-1", "Invoice-test-2", "Invoice-test-3");
+        List<String> invoices = incomingInvoiceFetcherService.getNewInvoices();
         delegateExecution.setVariableLocal("invoices", invoices);
     }
 }
diff --git a/lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/IncomingInvoiceFetcherService.java b/lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/IncomingInvoiceFetcherService.java
new file mode 100644 (file)
index 0000000..8137655
--- /dev/null
@@ -0,0 +1,62 @@
+package hu.user.lis.workflow.invoice.service;
+
+import hu.user.lis.db.Currency;
+import hu.user.lis.db.IncomingInvoice;
+import hu.user.lis.db.Partner;
+import hu.user.lis.db.repository.PartnerRepository;
+import hu.user.lis.services.data.DataGeneratorService;
+import org.apache.commons.lang3.RandomUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+@Service
+public class IncomingInvoiceFetcherService {
+    private static final SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
+    @Autowired
+    private PartnerRepository partnerRepository;
+    @Autowired
+    private DataGeneratorService dataGeneratorService;
+
+    public List<String> getNewInvoices() {
+        return Arrays.asList("Invoice-test-1", "Invoice-test-2", "Invoice-test-3");
+    }
+
+    public IncomingInvoice getInvoice(String humanId) throws Exception {
+        int currencyIndex = RandomUtils.nextInt(0, 3);
+        String title = dataGeneratorService.faker().commerce().productName();
+        Currency currency = Arrays.stream(Currency.values()).filter(p -> p.getVal() == currencyIndex).findFirst().orElse(Currency.HUF);
+        Date completionDate = dataGeneratorService.faker().date().between(formatter.parse("2010-01-01"), new Date());
+        Date createDate = dataGeneratorService.faker().date().past(RandomUtils.nextInt(6, 20), TimeUnit.DAYS, completionDate);
+        Date paymentDeadline = dataGeneratorService.faker().date().future(1, TimeUnit.DAYS, completionDate);
+        double netAmount = RandomUtils.nextFloat(5000, 1000000);
+        double grossAmount = netAmount * 1.27;
+        double vatAmount = grossAmount - netAmount;
+        return IncomingInvoice.builder()
+                .humanId(humanId)
+                .title(title)
+                .currency(currency)
+                .partner(getRandomPartner())
+                .completionDate(completionDate)
+                .createDate(createDate)
+                .paymentDeadline(paymentDeadline)
+                .completionDate(completionDate)
+                .createDate(createDate)
+                .paymentDeadline(paymentDeadline)
+                .netAmount(netAmount)
+                .grossAmount(grossAmount)
+                .vatAmount(vatAmount)
+                .income(true)
+                .build();
+    }
+
+    private Partner getRandomPartner() {
+        List<Partner> partners = partnerRepository.findAll();
+        return partners.get(RandomUtils.nextInt(0, partners.size()));
+    }
+}
diff --git a/lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/WorkflowManagerService.java b/lis-workflow/src/main/java/hu/user/lis/workflow/invoice/service/WorkflowManagerService.java
new file mode 100644 (file)
index 0000000..5e175ad
--- /dev/null
@@ -0,0 +1,100 @@
+package hu.user.lis.workflow.invoice.service;
+
+import hu.user.lis.workflow.event.ImportInvoicesEvent;
+import lombok.extern.log4j.Log4j2;
+import org.camunda.bpm.engine.RuntimeService;
+import org.camunda.bpm.engine.TaskService;
+import org.camunda.bpm.engine.impl.history.event.HistoryEvent;
+import org.camunda.bpm.engine.runtime.ProcessInstance;
+import org.camunda.bpm.engine.task.Task;
+import org.camunda.bpm.spring.boot.starter.event.ExecutionEvent;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.ApplicationEventPublisher;
+import org.springframework.context.event.EventListener;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.Map;
+
+@Log4j2
+@Service
+public class WorkflowManagerService {
+    @Autowired
+    private ApplicationEventPublisher applicationEventPublisher;
+
+    @Autowired
+    private RuntimeService runtimeService;
+
+    @Autowired
+    private TaskService taskService;
+
+    public void startImportIncomingInvoicesProcess() throws Exception {
+        long instanceCount = runtimeService.createProcessInstanceQuery().processDefinitionKey("importIncomingInvoices").active().count();
+        if (instanceCount > 0) {
+            throw new Exception("A számla import folyamat 1 példányban futhat!");
+        }
+        runtimeService.startProcessInstanceByKey("importIncomingInvoices");
+    }
+
+    public Map<String, Object> getVariables(String id) {
+        return taskService.getVariables(id);
+    }
+
+    public List<Task> listAssignInvoiceTasks() {
+        List<Task> taskList = taskService.createTaskQuery().taskDefinitionKey("assignProjectAndAttachFile").list();
+        return taskList;
+    }
+
+    public List<Task> listApproveInvoiceTasks() {
+        List<Task> taskList = taskService.createTaskQuery().taskDefinitionKey("approveAssignment").list();
+        return taskList;
+    }
+
+    public void cancelInvoiceImportProcess() {
+        List<ProcessInstance> processInstances = runtimeService.createProcessInstanceQuery().processDefinitionKey("importIncomingInvoices").list();
+        processInstances.forEach(p -> {
+            log.info("Killing {} {}", p.getProcessInstanceId(), p.getProcessDefinitionId());
+            try {
+                runtimeService.deleteProcessInstance(p.getProcessInstanceId(), "Canceled");
+            } catch (Exception e) {
+            }
+        });
+    }
+
+    @EventListener
+    public void onExecutionEvent(ExecutionEvent executionEvent) {
+//        log.info("ExecutionEvent {}", executionEvent.getEventName());
+//
+//        if ("assignProjectAndAttachFile".equals(executionEvent.getCurrentActivityId())) {
+//            ImportInvoicesEvent event = new ImportInvoicesEvent(this);
+//            event.setId(executionEvent.getProcessInstanceId());
+//            event.setEventType(executionEvent.getEventName());
+//            applicationEventPublisher.publishEvent(event);
+//        }
+//        if ("end".equals(executionEvent.getEventName())) {
+//        }
+    }
+
+    @EventListener
+    public void onHistoryEvent(HistoryEvent historyEvent) {
+        log.info("HistoryEvent {}", historyEvent.getEventType());
+        if ("importIncomingInvoices".equals(historyEvent.getProcessDefinitionKey())) {
+            ImportInvoicesEvent event = new ImportInvoicesEvent(this);
+            event.setId(historyEvent.getProcessInstanceId());
+            event.setKey(historyEvent.getProcessDefinitionKey());
+            event.setName(historyEvent.getProcessDefinitionName());
+            event.setEventType(historyEvent.getEventType());
+
+            applicationEventPublisher.publishEvent(event);
+        }
+
+    }
+
+    public void completeTask(String taskId) {
+        taskService.complete(taskId);
+    }
+
+    public void completeTask(String taskId, Map<String, Object> vars) {
+        taskService.complete(taskId, vars);
+    }
+}
index 4c010025d63939104b0e4dfbc9ef0fb43afae2ba..c33b4822a3ccecbead819fd12530d64015ba309c 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8"?>
-<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_07nj4df" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.14.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0">
+<bpmn:definitions xmlns:bpmn="http://www.omg.org/spec/BPMN/20100524/MODEL" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:camunda="http://camunda.org/schema/1.0/bpmn" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:modeler="http://camunda.org/schema/modeler/1.0" id="Definitions_07nj4df" targetNamespace="http://bpmn.io/schema/bpmn" exporter="Camunda Modeler" exporterVersion="5.14.0" modeler:executionPlatform="Camunda Platform" modeler:executionPlatformVersion="7.15.0">
   <bpmn:process id="importIncomingInvoices" name="Import Incoming Invoices" isExecutable="true" camunda:versionTag="0.1">
     <bpmn:startEvent id="StartEvent_1" camunda:asyncBefore="true">
       <bpmn:outgoing>Flow_1gzempz</bpmn:outgoing>
     <bpmn:serviceTask id="Activity_0q29ngn" name="List new invoices" camunda:asyncBefore="true" camunda:delegateExpression="${listNewInvoices}">
       <bpmn:extensionElements />
       <bpmn:incoming>Flow_1gzempz</bpmn:incoming>
-      <bpmn:outgoing>Flow_15jorzs</bpmn:outgoing>
+      <bpmn:outgoing>Flow_0y5cjps</bpmn:outgoing>
     </bpmn:serviceTask>
-    <bpmn:sequenceFlow id="Flow_15jorzs" sourceRef="Activity_0q29ngn" targetRef="Activity_02fjt6a" />
-    <bpmn:serviceTask id="Activity_02fjt6a" name="Download invoice datas" camunda:asyncBefore="true" camunda:delegateExpression="${downloadInvoiceData}">
-      <bpmn:extensionElements />
-      <bpmn:incoming>Flow_15jorzs</bpmn:incoming>
-      <bpmn:outgoing>Flow_1v1bne3</bpmn:outgoing>
-      <bpmn:multiInstanceLoopCharacteristics camunda:asyncBefore="true" camunda:collection="invoices" camunda:elementVariable="invoice" />
-    </bpmn:serviceTask>
-    <bpmn:endEvent id="Event_0ots211">
-      <bpmn:incoming>Flow_1v1bne3</bpmn:incoming>
+    <bpmn:subProcess id="assignAndApproveInvoices" name="Assign and approve">
+      <bpmn:incoming>Flow_0y5cjps</bpmn:incoming>
+      <bpmn:outgoing>Flow_0vgb0nw</bpmn:outgoing>
+      <bpmn:multiInstanceLoopCharacteristics camunda:collection="invoices" camunda:elementVariable="invoice" />
+      <bpmn:startEvent id="Event_1v3e1za">
+        <bpmn:outgoing>Flow_1ubjpz9</bpmn:outgoing>
+      </bpmn:startEvent>
+      <bpmn:endEvent id="Event_1cc91rc">
+        <bpmn:incoming>Flow_0uvj71q</bpmn:incoming>
+      </bpmn:endEvent>
+      <bpmn:sequenceFlow id="Flow_00r2v71" sourceRef="approveAssignment" targetRef="Gateway_1folrc8" />
+      <bpmn:userTask id="assignProjectAndAttachFile" name="Assign invoice">
+        <bpmn:extensionElements />
+        <bpmn:incoming>Flow_14m4iht</bpmn:incoming>
+        <bpmn:incoming>Flow_0efz1ox</bpmn:incoming>
+        <bpmn:outgoing>Flow_0c887e2</bpmn:outgoing>
+      </bpmn:userTask>
+      <bpmn:sequenceFlow id="Flow_0c887e2" sourceRef="assignProjectAndAttachFile" targetRef="approveAssignment" />
+      <bpmn:userTask id="approveAssignment" name="Approve invoice assignment">
+        <bpmn:incoming>Flow_0c887e2</bpmn:incoming>
+        <bpmn:outgoing>Flow_00r2v71</bpmn:outgoing>
+      </bpmn:userTask>
+      <bpmn:serviceTask id="Activity_02fjt6a" name="Download invoice datas" camunda:asyncBefore="true" camunda:delegateExpression="${downloadInvoiceData}">
+        <bpmn:extensionElements />
+        <bpmn:incoming>Flow_1ubjpz9</bpmn:incoming>
+        <bpmn:outgoing>Flow_14m4iht</bpmn:outgoing>
+      </bpmn:serviceTask>
+      <bpmn:sequenceFlow id="Flow_1ubjpz9" sourceRef="Event_1v3e1za" targetRef="Activity_02fjt6a" />
+      <bpmn:sequenceFlow id="Flow_14m4iht" sourceRef="Activity_02fjt6a" targetRef="assignProjectAndAttachFile" />
+      <bpmn:exclusiveGateway id="Gateway_1folrc8" default="Flow_0uvj71q">
+        <bpmn:incoming>Flow_00r2v71</bpmn:incoming>
+        <bpmn:outgoing>Flow_0uvj71q</bpmn:outgoing>
+        <bpmn:outgoing>Flow_0efz1ox</bpmn:outgoing>
+      </bpmn:exclusiveGateway>
+      <bpmn:sequenceFlow id="Flow_0uvj71q" sourceRef="Gateway_1folrc8" targetRef="Event_1cc91rc" />
+      <bpmn:sequenceFlow id="Flow_0efz1ox" sourceRef="Gateway_1folrc8" targetRef="assignProjectAndAttachFile">
+        <bpmn:conditionExpression xsi:type="bpmn:tFormalExpression">${not approved}</bpmn:conditionExpression>
+      </bpmn:sequenceFlow>
+    </bpmn:subProcess>
+    <bpmn:endEvent id="Event_07aly41">
+      <bpmn:incoming>Flow_0vgb0nw</bpmn:incoming>
     </bpmn:endEvent>
-    <bpmn:sequenceFlow id="Flow_1v1bne3" sourceRef="Activity_02fjt6a" targetRef="Event_0ots211" />
+    <bpmn:sequenceFlow id="Flow_0vgb0nw" sourceRef="assignAndApproveInvoices" targetRef="Event_07aly41" />
+    <bpmn:sequenceFlow id="Flow_0y5cjps" sourceRef="Activity_0q29ngn" targetRef="assignAndApproveInvoices" />
   </bpmn:process>
   <bpmndi:BPMNDiagram id="BPMNDiagram_1">
     <bpmndi:BPMNPlane id="BPMNPlane_1" bpmnElement="importIncomingInvoices">
       <bpmndi:BPMNShape id="_BPMNShape_StartEvent_2" bpmnElement="StartEvent_1">
-        <dc:Bounds x="179" y="99" width="36" height="36" />
+        <dc:Bounds x="179" y="159" width="36" height="36" />
       </bpmndi:BPMNShape>
       <bpmndi:BPMNShape id="Activity_0hxl7uq_di" bpmnElement="Activity_0q29ngn">
-        <dc:Bounds x="270" y="77" width="100" height="80" />
+        <dc:Bounds x="270" y="137" width="100" height="80" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Event_07aly41_di" bpmnElement="Event_07aly41">
+        <dc:Bounds x="1272" y="159" width="36" height="36" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Activity_0u274ph_di" bpmnElement="assignAndApproveInvoices" isExpanded="true">
+        <dc:Bounds x="420" y="77" width="800" height="203" />
+        <bpmndi:BPMNLabel />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Event_1v3e1za_di" bpmnElement="Event_1v3e1za">
+        <dc:Bounds x="460.33333333333337" y="159" width="36" height="36" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Activity_08g5whq_di" bpmnElement="assignProjectAndAttachFile">
+        <dc:Bounds x="710" y="137" width="100" height="80" />
+        <bpmndi:BPMNLabel />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Activity_003yviv_di" bpmnElement="approveAssignment">
+        <dc:Bounds x="870" y="137" width="100" height="80" />
+        <bpmndi:BPMNLabel />
       </bpmndi:BPMNShape>
       <bpmndi:BPMNShape id="Activity_035tjqs_di" bpmnElement="Activity_02fjt6a">
-        <dc:Bounds x="430" y="77" width="100" height="80" />
+        <dc:Bounds x="550" y="137" width="100" height="80" />
       </bpmndi:BPMNShape>
-      <bpmndi:BPMNShape id="Event_0ots211_di" bpmnElement="Event_0ots211">
-        <dc:Bounds x="592" y="99" width="36" height="36" />
+      <bpmndi:BPMNShape id="Gateway_1folrc8_di" bpmnElement="Gateway_1folrc8" isMarkerVisible="true">
+        <dc:Bounds x="1025" y="152" width="50" height="50" />
       </bpmndi:BPMNShape>
+      <bpmndi:BPMNShape id="Event_1cc91rc_di" bpmnElement="Event_1cc91rc">
+        <dc:Bounds x="1122" y="159" width="36" height="36" />
+      </bpmndi:BPMNShape>
+      <bpmndi:BPMNEdge id="Flow_00r2v71_di" bpmnElement="Flow_00r2v71">
+        <di:waypoint x="970" y="177" />
+        <di:waypoint x="1025" y="177" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="Flow_0c887e2_di" bpmnElement="Flow_0c887e2">
+        <di:waypoint x="810" y="177" />
+        <di:waypoint x="870" y="177" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="Flow_1ubjpz9_di" bpmnElement="Flow_1ubjpz9">
+        <di:waypoint x="496" y="177" />
+        <di:waypoint x="550" y="177" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="Flow_14m4iht_di" bpmnElement="Flow_14m4iht">
+        <di:waypoint x="650" y="177" />
+        <di:waypoint x="710" y="177" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="Flow_0uvj71q_di" bpmnElement="Flow_0uvj71q">
+        <di:waypoint x="1075" y="177" />
+        <di:waypoint x="1122" y="177" />
+      </bpmndi:BPMNEdge>
+      <bpmndi:BPMNEdge id="Flow_0efz1ox_di" bpmnElement="Flow_0efz1ox">
+        <di:waypoint x="1050" y="202" />
+        <di:waypoint x="1050" y="240" />
+        <di:waypoint x="760" y="240" />
+        <di:waypoint x="760" y="217" />
+      </bpmndi:BPMNEdge>
       <bpmndi:BPMNEdge id="Flow_1gzempz_di" bpmnElement="Flow_1gzempz">
-        <di:waypoint x="215" y="117" />
-        <di:waypoint x="270" y="117" />
+        <di:waypoint x="215" y="177" />
+        <di:waypoint x="270" y="177" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="Flow_15jorzs_di" bpmnElement="Flow_15jorzs">
-        <di:waypoint x="370" y="117" />
-        <di:waypoint x="430" y="117" />
+      <bpmndi:BPMNEdge id="Flow_0vgb0nw_di" bpmnElement="Flow_0vgb0nw">
+        <di:waypoint x="1220" y="177" />
+        <di:waypoint x="1272" y="177" />
       </bpmndi:BPMNEdge>
-      <bpmndi:BPMNEdge id="Flow_1v1bne3_di" bpmnElement="Flow_1v1bne3">
-        <di:waypoint x="530" y="117" />
-        <di:waypoint x="592" y="117" />
+      <bpmndi:BPMNEdge id="Flow_0y5cjps_di" bpmnElement="Flow_0y5cjps">
+        <di:waypoint x="370" y="177" />
+        <di:waypoint x="420" y="177" />
       </bpmndi:BPMNEdge>
     </bpmndi:BPMNPlane>
   </bpmndi:BPMNDiagram>