Резюмируя все вышесказанное, для того, чтобы автоматизировать решение поставленных задач, нужны мощная СУБД и система по разработке приложений. Основная часть существующих на сегодняшний момент СУБД обладают средствами разработки приложений, которые могут использоваться высококвалифицированными пользователями либо программистами для создания процедур автоматизации управления и дальнейшей обработки данных.
Увы, множество систем разработки приложений требуют наличия хотя бы минимального знания языка программирования. Чтобы успешно использовать их на практике нужно обладать определенным уровнем профессиональной подготовки и иметь опыт подобной работы.
Следует выделить главные сущности автоматизированный проектируемой системы и различных связей, которые существуют между ними. Ими можно воспользоваться для построения диаграммы «сущность-связь» (ERD). На ER-диаграмме отражены основные информационные объекты, характерные для предметной области (сущности), свойства (атрибуты) и отношения, которые существуют между ними.
2. ПРОЕКТНАЯ ЧАСТЬ
2.1 Информационная модель и ее описание
Задача разработки подсистемы управления проектами в ФКУ «Налог-сервис», позволит существенно упростить работу сотрудников по регистрации заявок, формированию договоров, учету проектов и их разработке. Написание прикладного решения, которое будет работать под управлением платформы 1С: Предприятие, состоит из двух этапов:
· моделирование функциональной структуры конфигурации;
· реализация конфигурации.
Исходя из вышеперечисленных требований в предыдущих разделах, построим модель функциональных модулей автоматизированной системы и их взаимодействие (рис. 4).
Рис. 4. Контекстная диаграмма автоматизированной системы
С точки зрения внешнего взаимодействия, система работает с двумя контрагентами, это:
· Заказчик;
· Сотрудники компании.
От заказчика система получает запрос на проведение разработки с указанием ответственного исполнителя, темы, даты предъявления готового продукта и комментариями.
Исполнитель при работе с системой получает всю необходимую информацию и по окончании работы отмечает проект, как выполненный.
2.2 Справочники автоматизированной системы управления проектами
Для сохранения данных в промежутках между сеансами работы программы, а также для обеспечения их независимости от таких обстоятельств, как ошибки пользователей, или сбой в сети электропитания, было решено использовать файл базы данных, хранимый на жестком диске с доступом к нему с помощью СУБД SQLite.
Для описания справочников автоматизированной системы управления проектами решено было использовать нотацию IDEF1X. IDEF1X является методом для разработки реляционных баз данных и использует условный синтаксис, специально разработанный для удобного построения концептуальной схемы. Концептуальной схемой мы называем универсальное представление структуры данных в рамках коммерческого предприятия, независимое от конечной реализации базы данных и аппаратной платформы. Будучи статическим методом разработки, IDEF1X изначально не предназначен для динамического анализа по принципу "AS IS", тем не менее, он иногда применяется в этом качестве, как альтернатива методу IDEF1. Использование метода IDEF1X наиболее целесообразно для построения логической структуры базы данных после того, как все информационные ресурсы исследованы (скажем с помощью метода IDEF1) и решение о внедрении реляционной базы данных, как части корпоративной информационной системы, было принято. Однако не стоит забывать, что средства моделирования IDEF1X специально разработаны для построения реляционных информационных систем, и если существует необходимость проектирования другой системы, скажем объектно-ориентированной, то лучше избрать другие методы моделирования.
Существует несколько очевидных причин, по которым IDEF1X не следует применять в случае построения нереляционных систем. Во-первых, IDEF1X требует от проектировщика определить ключевые атрибуты, для того чтобы отличить одну сущность от другой, в то время как объектно-ориентированные системы не требуют задания ключевых ключей, в целях идентифицирования объектов. Во-вторых, в тех случаях, когда более чем один атрибут является однозначно идентифицирующим сущность, проектировщик должен определить один из этих атрибутов первичным ключом, а все остальные вторичными. И, таким образом, построенная проектировщиком IDEF1X-модель и переданная для окончательной реализации программисту является некорректной для применения методов объектно-ориентированной реализации, и предназначена для построения реляционной системы.
Хотя терминология IDEF1X практически совпадает с терминологией IDEF1, существует ряд фундаментальных отличий в теоретических концепциях этих методологий. Сущность в IDEF1X описывает собой совокупность или набор экземпляров похожих по свойствам, но однозначно отличаемых друх от друга по одному или нескольким признакам. Каждый экземпляр является реализацией сущности. Таким образом, сущность в IDEF1X описывает конкретный набор экземпляров реального мира, в отличие от сущности в IDEF1, которая представляет собой абстрактный набор информационных отображений реального мира.
Основным преимуществом IDEF1X, по сравнению с другими многочисленными методами разработки реляционных баз данных, такими как ER и ENALIM является жесткая и строгая стандартизация моделирования. Установленные стандарты позволяют избежать различной трактовки построенной модели, которая несомненно является значительным недостатком ER.
Рис. 5. Информационная модель сущности «Клиенты» в таблице базы данных
Рис. 6. Информационная модель сущности «Исполнители» в таблице базы данных
Рис. 7. Информационная модель сущности «Задачи» в таблице базы данных
2.3 Программное обеспечение задачи разработки автоматизированной системы управления проектами
Структура разработанной системы представляет собой несколько связанных модулей, каждый из который выполняет свою определенную функцию, а также протоколы взаимодействия между модулями.
Модуль Authorization представляет собой окно авторизации, которое появляется автоматически при запуске программы и предотвращает использование программы сторонними пользователями. Два текстовых поля, отображаемые на экране, предназначены для ввода логина и пароля. При нажатии кнопки пара логин-пароль сравнивается с имеющимися в базе данных программы. В зависимости от нахождения совпадений модуль запускает один из двух других модулей: ClientMode или PerformerMode.
Листинг 1. FXML-представление окна модуля Authorization
<AnchorPane prefHeight="165.5" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.AuthorizationController"> <children> <Label layoutX="84.0" layoutY="14.0" text="Введите логин и пароль" /> <TextField fx:id="mLoginField" layoutX="50.0" layoutY="50.0" prefWidth="200.0" promptText="Логин" /> <TextField fx:id="mPassField" layoutX="50.0" layoutY="89.0" prefWidth="200.0" promptText="Пароль" /> <Button fx:id="mButton" layoutX="132.0" layoutY="131.0" mnemonicParsing="false" text="ОК" /> </children> </AnchorPane>
Листинг 2. Контроллер модуля Authorization
public class AuthorizationController implements Initializable {
@FXML
TextField mLoginField;
@FXML
TextField mPassField;
@FXML
Button mButton;
@Override
public void initialize(URL location, ResourceBundle resources) {
mButton.setOnAction(new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent event) {
Performer p = Db.getInstance().getPerformerWithName(mLoginField.getText());
if (p != null && mPassField.getText().equals(p.getPassword())) {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
"PerformerMode.fxml"
)
);
Stage stage = new Stage();
stage.setScene(
new Scene(
(Pane) loader.load()
)
);
PerformerModeController controller =
loader.<PerformerModeController>getController();
controller.setPerformer(p);
stage.show();
Stage stageToClose = (Stage) mButton.getScene().getWindow();
stageToClose.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Customer c = Db.getInstance().getCustomerWithName(mLoginField.getText());
if (c != null && mPassField.getText().equals(c.getPassword())) {
try {
FXMLLoader loader = new FXMLLoader(
getClass().getResource(
"ClientMode.fxml"
)
);
Stage stage = new Stage();
stage.setScene(
new Scene(
(Pane) loader.load()
)
);
ClientModeController controller =
loader.<ClientModeController>getController();
controller.setCustomer(c);
stage.show();
} catch (IOException e) {
e.printStackTrace();
}
}
}
});
}
}
Модуль ClientMode при запуске выводит на экран окно создания нового запроса на выполнение задания, в окне приложения при этом отображаются текстовые поля для ввода параметров задания, а также выпадающее меню для выбора исполнителя задания.
Листинг 3. FXML-представление окна модуля ClientMode
<AnchorPane prefHeight="297.0" prefWidth="300.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.ClientModeController"> <children> <Label layoutX="65.0" layoutY="39.0" text="Введите информацию о заказе" /> <TextField fx:id="theme" layoutX="50.0" layoutY="88.0" prefWidth="200.0" promptText="Тема" /> <TextField fx:id="deadlineDate" layoutX="51.0" layoutY="178.0" prefWidth="200.0" promptText="Дата окончания (MM.DD.YYYY)" /> <Button fx:id="button" layoutX="123.0" layoutY="238.0" mnemonicParsing="false" text="ОК" /> <MenuButton fx:id="performer" layoutX="96.0" layoutY="139.0" mnemonicParsing="false" text="Исполнитель"> <items> <MenuItem mnemonicParsing="false" text="Иван" fx:id="item1" /> <MenuItem mnemonicParsing="false" text="Дмитрий" fx:id="item2" /> </items> </MenuButton> </children> </AnchorPane>
Листинг 4. Контроллер модуля ClientMode
public class ClientModeController implements Initializable { @FXML TextField theme; @FXML TextField deadlineDate; @FXML MenuButton performer; @FXML Button button; @FXML MenuItem item1; @FXML MenuItem item2; private Performer mPerformer = null; private Customer mCustomer; @Override public void initialize(URL location, ResourceBundle resources) { item1.setOnAction(event -> { mPerformer = Db.getInstance().getPerformerWithName("p1"); performer.setText("Иван"); }); item2.setOnAction(event -> { mPerformer = Db.getInstance().getPerformerWithName("p2"); performer.setText("Дмитрий"); }); button.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { try { Job job = new Job( new Date(), DateFormatter.FORMATTER.parse(deadlineDate.getText()), theme.getText(), mPerformer, mCustomer ); Db.getInstance().addJob(job); ((Stage) button.getScene().getWindow()).close(); } catch (ParseException e) { e.printStackTrace(); } } }); } public void setCustomer(Customer customer) { mCustomer = customer; } }
Модуль PerformerMode является основным с точки зрения предназначения приложения. Главным элементов окна программы является таблица, в которую автоматически заносится список задач. В колонках таблицы отображаются основные параметры каждой задачи. Двойной клик на строке таблицы запускает модуль просмотра детальной информации о выделенной задаче.
Листинг 5. FXML-представление окна модуля PerformerMode
<AnchorPane prefHeight="341.0" prefWidth="482.0" xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.PerformerModeController"> <children> <TableView fx:id="tableView" layoutX="0.0" layoutY="0.0" prefHeight="284.0" prefWidth="482.0"> <columns> <TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="124.0" text="Дата создания" fx:id="startDate" /> <TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="108.0" text="Тема" fx:id="theme" /> <TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="122.0" text="Инициатор" fx:id="customer" /> <TableColumn maxWidth="5000.0" minWidth="10.0" prefWidth="128.0" text="Срок выполнения" fx:id="deadlineDate" /> </columns> </TableView> <Button fx:id="updateButton" layoutX="149.0" layoutY="295.0" mnemonicParsing="false" prefWidth="104.0" text="Обновить" /> </children> </AnchorPane>
Листинг 6. Контроллер модуля PerformerMode
public class PerformerModeController implements Initializable { @FXML TableView tableView; @FXML TableColumn startDate; @FXML TableColumn deadlineDate; @FXML TableColumn theme; @FXML TableColumn customer; @FXML Button updateButton; private Performer mPerformer; ObservableList<JobWrapper> data = FXCollections.observableArrayList(); @Override public void initialize(URL location, ResourceBundle resources) { startDate.setCellValueFactory( new PropertyValueFactory<>("creatingTimestamp") ); startDate.setCellFactory(TextFieldTableCell.<JWindow>forTableColumn()); deadlineDate.setCellValueFactory( new PropertyValueFactory<>("deadline") ); deadlineDate.setCellFactory(TextFieldTableCell.<JWindow>forTableColumn()); theme.setCellValueFactory( new PropertyValueFactory<>("theme") ); theme.setCellFactory(TextFieldTableCell.<JWindow>forTableColumn()); customer.setCellValueFactory( new PropertyValueFactory<>("customerName") ); customer.setCellFactory(TextFieldTableCell.<JWindow>forTableColumn()); ArrayList<JobWrapper> wrappers = Db.getInstance().getJobs(); tableView.setRowFactory( tv -> { TableRow<JobWrapper> row = new TableRow<>(); row.setOnMouseClicked(event -> { if (event.getClickCount() == 2 && (! row.isEmpty()) ) { JobWrapper selectedJob = row.getItem(); try { FXMLLoader loader = new FXMLLoader( getClass().getResource( "JobDetails.fxml" ) ); Stage stage = new Stage(); stage.setScene( new Scene( (Pane) loader.load() ) ); JobDetailsController controller = loader.<JobDetailsController>getController(); controller.setPerformer(mPerformer); controller.setJob(selectedJob.getJob()); stage.show(); } catch (IOException e) { e.printStackTrace(); } } }); return row ; }); data.addAll(wrappers); tableView.setItems(data); updateButton.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { data.clear(); data.addAll(Db.getInstance().getJobs()); tableView.setItems(data); } }); } public void setPerformer(Performer performer) { mPerformer = performer; } }
Модуль JobDetails представляет собой окно просмотра детальных параметров задачи для ознакомления с ним исполнителя. Также модуль предоставляет исполнителю возможность присвоить задаче статус выполненного. Эта функция носит условный характер, т.к. возможность выполнения задачи предусмотрена только для исполнителя, выбранного заказчиком при создании задания.
Листинг 7. FXML-представление окна модуля JobDetails
ns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2" fx:controller="sample.JobDetailsController"> <children> <Label layoutX="14.0" layoutY="14.0" text="Дата создания" /> <Label layoutX="14.0" layoutY="47.0" text="Срок выполнения" /> <Label layoutX="14.0" layoutY="80.0" text="Тема" /> <Label layoutX="14.0" layoutY="114.0" text="Заказчик" /> <Label layoutX="14.0" layoutY="152.0" text="Исполнитель" /> <Label fx:id="startDate" layoutX="154.0" layoutY="14.0" text="Label" /> <Label fx:id="deadlineDate" layoutX="154.0" layoutY="47.0" text="Label" /> <Label fx:id="theme" layoutX="154.0" layoutY="80.0" text="Label" /> <Label fx:id="customerName" layoutX="154.0" layoutY="111.0" text="Label" /> <Label fx:id="performerName" layoutX="154.0" layoutY="152.0" text="Label" /> <Button fx:id="btn" layoutX="108.0" layoutY="200.0" mnemonicParsing="false" prefWidth="140.0" text="Завершить задание" /> </children> </AnchorPane>
Листинг 8. Контроллер модуля JobDetails
public class JobDetailsController implements Initializable { @FXML Label startDate; @FXML Label deadlineDate; @FXML Label theme; @FXML Label customerName; @FXML Label performerName; @FXML Button btn; private Job mJob; private Performer mPerformer; @Override public void initialize(URL location, ResourceBundle resources) { btn.setDisable(true); btn.setOnAction(new EventHandler<ActionEvent>() { @Override public void handle(ActionEvent event) { Db.getInstance().removeJob(mJob); ((Stage) btn.getScene().getWindow()).close(); } }); } public void setJob(Job job) { mJob = job; startDate.setText(DateFormatter.FORMATTER.format(mJob.getCreatingDate())); deadlineDate.setText(DateFormatter.FORMATTER.format(mJob.getDeadlineDate())); theme.setText(mJob.getTheme()); customerName.setText(mJob.getCustomer().getName()); performerName.setText(mJob.getPerformer().getName()); if (mPerformer != null) { if (mJob.getPerformer().equals(mPerformer)) { btn.setDisable(false); } } } public void setPerformer(Performer performer) { mPerformer = performer; } }