Skip to content

Commit

Permalink
Merge pull request #1 from alyf-de/develop
Browse files Browse the repository at this point in the history
feat: v0.0.1
  • Loading branch information
scdanieli authored Feb 28, 2023
2 parents db9f384 + d811713 commit 035db89
Show file tree
Hide file tree
Showing 19 changed files with 650 additions and 1 deletion.
2 changes: 1 addition & 1 deletion working_time/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@
# ------------

# before_install = "working_time.install.before_install"
# after_install = "working_time.install.after_install"
after_install = "working_time.install.after_install"

# Uninstallation
# ------------
Expand Down
87 changes: 87 additions & 0 deletions working_time/install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Copyright (c) 2023, ALYF GmbH and contributors
# For license information, please see license.txt

import frappe
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields


def after_install():
customize_project()
customize_timesheet()
insert_docs()


def customize_project():
custom_fields = {
"Project": [
{
"fieldname": "billing_rate",
"label": "Billing Rate per Hour",
"fieldtype": "Currency",
"options": "currency",
"insert_after": "cost_center",
"translatable": 0,
},
{
"fieldname": "jira_section",
"label": "Jira",
"fieldtype": "Section Break",
"insert_after": "message",
"collapsible": 1,
},
{
"fieldname": "jira_site",
"label": "Site",
"fieldtype": "Link",
"options": "Jira Site",
"insert_after": "jira_section",
"translatable": 0,
},
]
}

create_custom_fields(custom_fields)


def customize_timesheet():
custom_fields = {
"Timesheet Detail": [
{
"fieldname": "jira_section",
"label": "Jira",
"fieldtype": "Section Break",
"insert_after": "costing_amount",
},
{
"fieldname": "jira_issue_url",
"label": "Issue URL",
"fieldtype": "Data",
"Options": "URL",
"insert_after": "jira_section",
"read_only": 1,
"translatable": 0,
},
]
}

create_custom_fields(custom_fields)


def insert_docs():
docs = [
{
"doctype": "Activity Type",
"activity_type": "Default",
}
]

for doc in docs:
filters = doc.copy()

# Clean up filters. They need to be a plain dict without nested dicts or lists.
for key, value in doc.items():
if isinstance(value, (list, dict)):
del filters[key]

if not frappe.db.exists(filters):
frappe.get_doc(doc).insert(ignore_if_duplicate=True)
47 changes: 47 additions & 0 deletions working_time/jira_client.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Copyright (c) 2023, ALYF GmbH and contributors
# For license information, please see license.txt


import json

import frappe
from frappe import _
import requests
from requests.auth import HTTPBasicAuth


class JiraClient:
def __init__(self, jira_site: str) -> None:
jira_site = frappe.get_doc("Jira Site", jira_site)

self.url = f"https://{jira_site.name}"
self.session = requests.Session()
self.session.auth = HTTPBasicAuth(
jira_site.username, jira_site.get_password(fieldname="api_token")
)
self.session.headers = {"Accept": "application/json"}

def get(self, url: str, params=None):
response = self.session.get(url, params=params)

try:
response.raise_for_status()
except requests.HTTPError:
error_text = json.loads(response.text)
error_message = (
error_text.get("errorMessage")
or (error_text.get("errorMessages") or [None])[0]
or "Something went wrong."
)

frappe.throw(f"{url}: {_(error_message)}")

return response.json()

def get_issue_summary(self, key: str) -> str:
url = f"{self.url}/rest/api/2/issue/{key}"
params = {
"fields": "summary",
}

return self.get(url, params=params).get("fields").get("summary")
1 change: 1 addition & 0 deletions working_time/patches.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
working_time.patches.migrate_old_working_times
16 changes: 16 additions & 0 deletions working_time/patches/migrate_old_working_times.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import frappe


def execute():
doc_names = frappe.get_all("Working Time", pluck="name")

for doc_name in doc_names:
date, break_time, working_time = frappe.db.get_value(
"Working Time", doc_name, ["from_date", "break_duration", "total_duration"]
)
frappe.db.set_value(
"Working Time",
doc_name,
{"date": date, "break_time": break_time, "working_time": working_time},
update_modified=False,
)
Empty file.
Empty file.
8 changes: 8 additions & 0 deletions working_time/working_time/doctype/jira_site/jira_site.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Copyright (c) 2023, ALYF GmbH and contributors
// For license information, please see license.txt

frappe.ui.form.on('Jira Site', {
// refresh: function(frm) {

// }
});
71 changes: 71 additions & 0 deletions working_time/working_time/doctype/jira_site/jira_site.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
{
"actions": [],
"allow_rename": 1,
"autoname": "field:site_url",
"creation": "2023-01-30 10:07:01.823009",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"site_url",
"column_break_2",
"username",
"column_break_4",
"api_token"
],
"fields": [
{
"description": "e.g. your-domain.atlassian.net",
"fieldname": "site_url",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Site URL",
"reqd": 1,
"unique": 1
},
{
"fieldname": "column_break_2",
"fieldtype": "Section Break"
},
{
"fieldname": "username",
"fieldtype": "Data",
"label": "Username",
"reqd": 1
},
{
"fieldname": "api_token",
"fieldtype": "Password",
"label": "API Token",
"reqd": 1
},
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
}
],
"index_web_pages_for_search": 1,
"links": [],
"modified": "2023-01-30 10:13:44.680028",
"modified_by": "Administrator",
"module": "Working Time",
"name": "Jira Site",
"owner": "Administrator",
"permissions": [
{
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"share": 1,
"write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "site_url"
}
8 changes: 8 additions & 0 deletions working_time/working_time/doctype/jira_site/jira_site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2023, ALYF GmbH and contributors
# For license information, please see license.txt

# import frappe
from frappe.model.document import Document

class JiraSite(Document):
pass
8 changes: 8 additions & 0 deletions working_time/working_time/doctype/jira_site/test_jira_site.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2023, ALYF GmbH and Contributors
# See license.txt

# import frappe
import unittest

class TestJiraSite(unittest.TestCase):
pass
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Copyright (c) 2023, ALYF GmbH and Contributors
# See license.txt

# import frappe
import unittest

class TestWorkingTime(unittest.TestCase):
pass
17 changes: 17 additions & 0 deletions working_time/working_time/doctype/working_time/working_time.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Copyright (c) 2023, ALYF GmbH and contributors
// For license information, please see license.txt

frappe.ui.form.on("Working Time", {
setup: function (frm) {
frm.set_query("employee", "erpnext.controllers.queries.employee_query");
},
});

frappe.ui.form.on("Working Time Log", {
time_logs_add: function (frm, cdt, cdn) {
let row = locals[cdt][cdn];
row.from_time = frappe.datetime.now_time(false);
row.to_time = ""; // Otherwise Frappe may overwrite empty values with the current time on save.
frm.refresh_field("time_logs");
},
});
Loading

0 comments on commit 035db89

Please sign in to comment.